This document explains how aimemo works with OpenClaw skills, including the complete architecture, data flow, and runtime behavior.
- Architecture Overview
- Initialization Flow
- Runtime Call Chain
- File System Layout
- Memory Isolation
- Data Flow
- Best Practices
- Troubleshooting
┌──────────────────────────────────────────────────────────────────┐
│ OpenClaw Gateway Process │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Agent Runtime │ │
│ │ │ │
│ │ User Request │ │
│ │ ↓ │ │
│ │ [Agent matches request to skill] │ │
│ │ ↓ │ │
│ │ ┌─────────────────────────────────────────────┐ │ │
│ │ │ Skill: github-pr-reviewer │ │ │
│ │ │ (SKILL.md instructions) │ │ │
│ │ │ │ │ │
│ │ │ → Load memory: │ │ │
│ │ │ memory_context({ │ │ │
│ │ │ context: "github-pr-reviewer" │ │ │
│ │ │ }) │ │ │
│ │ └──────────────────┬──────────────────────────┘ │ │
│ │ │ │ │
│ │ │ JSON-RPC over stdio │ │
│ │ ▼ │ │
│ │ ┌──────────────────────────────────────────────────┐ │ │
│ │ │ MCP Client (Gateway-managed) │ │ │
│ │ │ │ │ │
│ │ │ Request: │ │ │
│ │ │ { │ │ │
│ │ │ "method": "tools/call", │ │ │
│ │ │ "params": { │ │ │
│ │ │ "name": "memory_context", │ │ │
│ │ │ "arguments": { │ │ │
│ │ │ "context": "github-pr-reviewer" │ │ │
│ │ │ } │ │ │
│ │ │ } │ │ │
│ │ │ } │ │ │
│ │ └───────────────────┬──────────────────────────────┘ │ │
│ └────────────────────────┼─────────────────────────────────┘ │
│ │ │
│ │ stdio (stdin/stdout) │
│ │ │
└───────────────────────────┼──────────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────────────┐
│ aimemo serve (MCP Server) │
│ CWD: ~/.openclaw/workspace │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ 1. Receive MCP request │ │
│ │ {context: "github-pr-reviewer"} │ │
│ │ │ │
│ │ 2. Call FindProjectDB("github-pr-reviewer") │ │
│ │ → Walk up from CWD looking for .aimemo/ │ │
│ │ → Found: ~/.openclaw/workspace/.aimemo/ │ │
│ │ → Return: .../memory-github-pr-reviewer.db │ │
│ │ │ │
│ │ 3. Open database │ │
│ │ db.Open(".../memory-github-pr-reviewer.db") │ │
│ │ │ │
│ │ 4. Execute query │ │
│ │ - Calculate importance score (recency + access) │ │
│ │ - BM25 full-text search │ │
│ │ - Return top 20 observations │ │
│ │ │ │
│ │ 5. Return result │ │
│ │ { │ │
│ │ "content": [{ │ │
│ │ "type": "text", │ │
│ │ "text": "Last session: ...\n │ │
│ │ Preferences: snake_case, no trailing ," │ │
│ │ }] │ │
│ │ } │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
└────────────────────────────┬───────────────────────────────────────┘
│
│ JSON-RPC Response
▼
┌─────────────────────────────────────────────────────────────────┐
│ Agent receives memory context │
│ │
│ → Use loaded preferences to perform task │
│ → Store new discoveries: │
│ │
│ memory_store({ │
│ context: "github-pr-reviewer", │
│ entities: [{ │
│ name: "code-style-preferences", │
│ entityType: "preferences", │
│ observations: ["User prefers early returns"] │
│ }] │
│ }) │
└─────────────────────────────────────────────────────────────────┘
- OpenClaw Gateway: Manages the agent lifecycle and MCP server connections
- Agent Runtime: Executes skills based on user requests
- MCP Client: Bridges skills to MCP servers via JSON-RPC
- aimemo serve: MCP server providing memory tools
- SQLite Database: Persistent storage for each skill's memory
┌────────────────────────────────────────────────────────────┐
│ Step 1: Install aimemo │
│ │
│ Linux/macOS: │
│ curl -sSL https://raw.githubusercontent.com/MyAgentHubs/aimemo/main/install.sh | bash │
│ │
│ Or via Homebrew (macOS): │
│ brew install MyAgentHubs/tap/aimemo │
│ │
│ Verify: │
│ aimemo --version │
└────────────────────────────────────────────────────────────┘
↓
┌────────────────────────────────────────────────────────────┐
│ Step 2: Register MCP server with OpenClaw │
│ │
│ claude mcp add-json aimemo-memory \ │
│ '{"command":"aimemo","args":["serve"]}' │
│ │
│ Or add to ~/.openclaw/openclaw.json: │
│ { │
│ "mcpServers": { │
│ "aimemo-memory": { │
│ "command": "aimemo", │
│ "args": ["serve"] │
│ } │
│ } │
│ } │
└────────────────────────────────────────────────────────────┘
↓
┌────────────────────────────────────────────────────────────┐
│ Step 3: Initialize workspace memory │
│ │
│ cd ~/.openclaw/workspace │
│ aimemo init │
│ │
│ Creates: ~/.openclaw/workspace/.aimemo/memory.db │
└────────────────────────────────────────────────────────────┘
↓
┌────────────────────────────────────────────────────────────┐
│ Step 4: Restart OpenClaw │
│ │
│ The Gateway will now spawn aimemo serve on startup │
└────────────────────────────────────────────────────────────┘
If a skill wants isolated memory, it should declare in SKILL.md:
## Setup
This skill uses aimemo for persistent memory. The memory is automatically
isolated using the context parameter - no manual setup required.
On first use, aimemo will create:
~/.openclaw/workspace/.aimemo/memory-{skill-name}.dbUser says: "Review this PR with my code style preferences"
↓
┌─────────────────────────────────────────────────────────┐
│ 1. OpenClaw Agent matches to github-pr-reviewer skill │
└───────────────────────┬─────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ 2. Skill instruction: Load memory first │
│ → Call memory_context({context: "github-pr-..."}) │
└───────────────────────┬─────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ 3. MCP Client sends JSON-RPC request to aimemo serve │
│ Protocol: stdio (stdin/stdout) │
└───────────────────────┬─────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ 4. aimemo serve processes request: │
│ a. Parse context: "github-pr-reviewer" │
│ b. Find DB: memory-github-pr-reviewer.db │
│ c. Query: SELECT with importance scoring │
│ d. Return: Top observations + journal entries │
└───────────────────────┬─────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ 5. Agent receives memory context │
│ Example: "Last session: prefer snake_case..." │
└───────────────────────┬─────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ 6. Agent performs task with loaded context │
│ → Fetch PR code │
│ → Review with learned preferences │
│ → Provide feedback to user │
└───────────────────────┬─────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ 7. Agent stores new learnings │
│ → Call memory_store({context: "...", entities: ...}) │
└───────────────────────┬─────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ 8. aimemo persists to SQLite │
│ → Update observations table │
│ → Update access timestamps │
│ → Recalculate importance scores │
└─────────────────────────────────────────────────────────┘
- MCP call overhead: < 10ms
- Memory query (cold): < 50ms
- Memory query (cached): < 5ms
- Memory store: < 20ms
~/.openclaw/
├── openclaw.json # OpenClaw config (MCP registration)
│ {
│ "mcpServers": {
│ "aimemo-memory": {
│ "command": "aimemo",
│ "args": ["serve"]
│ }
│ }
│ }
│
└── workspace/
├── .aimemo/ # aimemo memory root
│ ├── memory.db # Default/shared memory
│ ├── memory-github-pr-reviewer.db # ← Skill A
│ ├── memory-slack-notifier.db # ← Skill B
│ └── memory-jira-automation.db # ← Skill C
│
└── skills/ # OpenClaw skills
├── github-pr-reviewer/
│ └── SKILL.md # ← Declares aimemo usage
│
├── slack-notifier/
│ └── SKILL.md
│
└── jira-automation/
└── SKILL.md
- No context parameter:
memory.db - With context parameter:
memory-{context}.db
Example:
// Uses memory.db (default/shared)
memory_context({})
// Uses memory-github-pr-reviewer.db (isolated)
memory_context({context: "github-pr-reviewer"})Context Name Requirements:
- Context names are sanitized to
[a-z0-9-](lowercase alphanumeric and hyphens) - Invalid characters are replaced with
-, leading/trailing-are trimmed - Empty or invalid context names default to
memory.db
Examples:
"My-Skill" → memory-my-skill.db
"skill_name" → memory-skill-name.db
"SKILL@123" → memory-skill-123.db
"---" → memory.db (sanitizes to empty, uses default)
┌────────────────────────────────────────────────────────┐
│ Skill A: github-pr-reviewer │
│ context: "github-pr-reviewer" │
│ ↓ │
│ memory-github-pr-reviewer.db │
│ ├─ code-style-preferences │
│ ├─ review-patterns │
│ └─ user-feedback-history │
│ │
│ ✅ Only sees its own data │
│ ❌ Cannot see slack-notifier's data │
└────────────────────────────────────────────────────────┘
┌────────────────────────────────────────────────────────┐
│ Skill B: slack-notifier │
│ context: "slack-notifier" │
│ ↓ │
│ memory-slack-notifier.db │
│ ├─ notification-preferences │
│ ├─ sent-message-hashes (dedup) │
│ └─ user-timezone-settings │
│ │
│ ✅ Only sees its own data │
│ ❌ Cannot see github-pr-reviewer's data │
└────────────────────────────────────────────────────────┘
- Context Parameter: Each skill passes its unique identifier
- Separate Databases: Different
.dbfiles prevent cross-contamination - FTS Isolation: Full-text search only queries the skill's database
- No Shared State: Skills cannot accidentally read/write each other's memory
Use the default database (no context parameter) for:
- Cross-skill shared knowledge
- Project-wide facts
- User preferences that apply globally
Example:
// Shared: all skills can access
memory_store({
entities: [{
name: "user-timezone",
entityType: "preference",
observations: ["UTC+8"]
}]
})
// Isolated: only slack-notifier can access
memory_store({
context: "slack-notifier",
entities: [{
name: "notification-times",
entityType: "schedule",
observations: ["Send daily digest at 9am"]
}]
})Skill discovers new pattern
↓
memory_store({
context: "github-pr-reviewer",
entities: [{
name: "code-style",
entityType: "preferences",
observations: ["Use early returns"]
}]
})
↓
┌─────────────────────────────────────────┐
│ aimemo MCP server │
│ │
│ 1. Parse request │
│ context = "github-pr-reviewer" │
│ │
│ 2. Open/create DB │
│ memory-github-pr-reviewer.db │
│ │
│ 3. Check if entity exists │
│ SELECT * WHERE name='code-style' │
│ │
│ 4. Upsert entity │
│ INSERT OR UPDATE │
│ │
│ 5. Add observations │
│ - Deduplicate │
│ - Update timestamps │
│ - Recalculate importance │
│ │
│ 6. Update FTS index │
│ INSERT INTO fts_observations │
└─────────────────────────────────────────┘
↓
Database persisted
Skill starts work
↓
memory_context({
context: "github-pr-reviewer"
})
↓
┌─────────────────────────────────────────┐
│ aimemo MCP server │
│ │
│ 1. Open DB │
│ memory-github-pr-reviewer.db │
│ │
│ 2. Calculate importance scores │
│ score = 0.6/LOG(hours_ago+2) │
│ + 0.4*LOG(access_count+1) │
│ │
│ 3. Query recent + important │
│ - Last 24h by default │
│ - Top 20 by importance │
│ - Include journal entries │
│ │
│ 4. Format response │
│ - Group by entity │
│ - Show relationships │
│ - Include metadata │
└─────────────────────────────────────────┘
↓
Skill receives context
Skill needs specific info
↓
memory_search({
context: "github-pr-reviewer",
query: "error handling"
})
↓
┌─────────────────────────────────────────┐
│ aimemo MCP server │
│ │
│ 1. Open DB │
│ memory-github-pr-reviewer.db │
│ │
│ 2. FTS5 search │
│ SELECT * FROM fts_observations │
│ WHERE fts_observations MATCH 'error' │
│ AND fts_observations MATCH 'handling'│
│ │
│ 3. BM25 ranking │
│ - Term frequency │
│ - Inverse document frequency │
│ - Document length normalization │
│ │
│ 4. Apply importance weights │
│ final_score = bm25 * importance │
│ │
│ 5. Return top results │
│ - Limit: 10 (default) │
│ - Include context snippets │
└─────────────────────────────────────────┘
↓
Skill receives search results
✅ GOOD:
memory_context({context: "my-skill-name"})
❌ BAD:
memory_context({}) // Uses shared DB, risks collision## Instructions
When the user asks you to [do task]:
1. **FIRST**: Load memory
memory_context({context: "my-skill-name"})
2. Then proceed with the task
3. Store learnings before finishing// ✅ Store as you learn
memory_store({
context: "my-skill",
entities: [{name: "pattern-1", ...}]
})
// Later...
memory_store({
context: "my-skill",
entities: [{name: "pattern-2", ...}]
})
// ❌ Don't try to store everything at once
// (risks losing data if interrupted)// At session end
memory_store({
context: "my-skill",
journal: "Completed: task X. In progress: task Y. Blocker: Z."
})// ✅ GOOD: Clear, searchable
{name: "user-code-style-preferences", entityType: "preferences"}
{name: "common-bug-patterns", entityType: "knowledge"}
// ❌ BAD: Vague, hard to search
{name: "data", entityType: "stuff"}
{name: "temp", entityType: "thing"}# Backup all skill memories
tar -czf aimemo-backup-$(date +%Y%m%d).tar.gz \
~/.openclaw/workspace/.aimemo/# Check size of all memory databases
du -sh ~/.openclaw/workspace/.aimemo/*.db# List memories for a specific skill
aimemo list --context github-pr-reviewer
# Search within a skill's memory
aimemo search "error handling" --context github-pr-reviewer
# Export for inspection
aimemo export --context github-pr-reviewer --format json > memory.json# Soft-delete an entity (recoverable)
aimemo forget entity-name --context github-pr-reviewer
# Hard-delete permanently
aimemo forget entity-name --context github-pr-reviewer --permanentSymptoms: Error like "tool 'memory_context' not found"
Solutions:
-
Verify MCP server registration:
cat ~/.openclaw/openclaw.json | grep aimemo
-
Check if aimemo is in PATH:
which aimemo
-
Try absolute path in config:
{ "mcpServers": { "aimemo-memory": { "command": "/usr/local/bin/aimemo", "args": ["serve"] } } }
Symptoms: Skill seems to forget learnings between sessions
Solutions:
-
Verify database exists:
ls -lh ~/.openclaw/workspace/.aimemo/ -
Check database is writable:
touch ~/.openclaw/workspace/.aimemo/test && rm ~/.openclaw/workspace/.aimemo/test
-
Verify skill is using correct context:
- Check SKILL.md instructions
- Ensure context parameter matches skill name
Symptoms: Skill A sees data from Skill B
Solution:
- Both skills are missing context parameter
- Add
context: "skill-name"to all memory tool calls
Symptoms: OpenClaw logs show "failed to start MCP server"
Solutions:
-
Test aimemo serve manually:
cd ~/.openclaw/workspace aimemo serve # Should wait for stdin, not exit immediately
-
Check aimemo version:
aimemo --version # Should be v0.4.0 or later for OpenClaw support -
Restart OpenClaw Gateway:
# macOS launchctl stop com.openclaw.gateway launchctl start com.openclaw.gateway # Linux systemctl --user restart openclaw-gateway
Symptoms: Error like "database disk image is malformed"
Solutions:
-
Try SQLite recovery:
cd ~/.openclaw/workspace/.aimemo sqlite3 memory-skill-name.db ".recover" | sqlite3 memory-skill-name-recovered.db mv memory-skill-name.db memory-skill-name.db.backup mv memory-skill-name-recovered.db memory-skill-name.db
-
If unrecoverable, restore from backup or reinitialize:
rm ~/.openclaw/workspace/.aimemo/memory-skill-name.db # Skill will auto-create on next use
Symptoms: memory_search returns unrelated observations
Solutions:
-
Use more specific queries:
// ❌ Too broad memory_search({query: "code"}) // ✅ Specific memory_search({query: "error handling patterns"})
-
Filter by entity type:
memory_search({ query: "preferences", type: "preferences" // Only search preferences })
-
Use tags for better organization:
memory_store({ entities: [{ name: "style-rule-1", observations: ["..."], tags: ["code-style", "naming"] }] }) // Later search by tag memory_search({tags: ["code-style"]})
You can create hierarchical memory by using dotted context names:
// Project-level memory
memory_context({context: "myproject"})
// Feature-specific memory
memory_context({context: "myproject.auth"})
memory_context({context: "myproject.billing"})Note: These are still independent databases - aimemo doesn't currently support hierarchical queries.
If two skills need to share specific knowledge:
-
Option A: Use a shared context
// Both skills use same context memory_store({ context: "shared-knowledge", entities: [...] })
-
Option B: Use the default database
// No context = shared memory_store({ entities: [...] })
For skills with large memories (>1000 entities):
-
Adjust limit in queries:
memory_context({ context: "my-skill", limit: 50 // Default is 20 })
-
Use targeted searches:
// Instead of loading all context memory_search({ context: "my-skill", query: "specific topic", limit: 5 })
-
Archive old data:
# Export old memories aimemo export --context my-skill > backup.json # Clear database rm ~/.openclaw/workspace/.aimemo/memory-my-skill.db # Re-import only recent data # (manual filtering required)
- OpenClaw Integration Guide - Step-by-step setup
- Main README - aimemo overview
- CLI Reference - Command-line tools
- Example Skill - Complete working example