How It Works โ
This page explains the end-to-end memory flow โ from a conversation happening to a memory being available in the next session.
Design at a Glance โ
| Design Principle | What It Means | Key Mechanism |
|---|---|---|
| Zero blind spots across session resets | No conversation is lost when a session ends, even mid-day | Today's diary + yesterday's diary + mem0 combined cover every time window |
| Two-tier memory: short-term โ long-term | Recent content stays isolated; only proven facts graduate to long-term | auto_digest writes with run_id=today (bounded dedup); auto_dream promotes at UTC 02:00 (global dedup) |
| Real-time diary capture | Every conversation turn is captured immediately | openclaw-plugin agent_end hook writes diary in real-time; no polling delay |
| Semantic retrieval, not keyword search | Find memories by meaning, not exact phrasing | Vector similarity + time-decay blended ranking (0.7 ร score + 0.3 ร recency) |
| Cross-agent knowledge sharing | Lessons learned by one agent instantly benefit all agents | category=experience / procedural auto-writes to shared pool; every search includes shared pool |
| Write generously, dedup automatically | Agents don't need to worry about redundancy โ mem0 handles it | infer=True triggers internal fact extraction; same-day dedup is bounded by run_id |
| Targeted extraction per category | Different memory types need different extraction angles | custom_extraction_prompt per /memory/add call; auto_digest uses DIGEST_EXTRACTION_PROMPT for technical context preservation + TASK_EXTRACTION_PROMPT for task extraction on every session block |
| Three paths to long-term memory | Different sources land in long-term at different speeds | Explicit CLI write (immediate) โ memory_sync (same day) โ auto_dream (7-day cadence) |
| Multi-session, multi-agent continuity | What happens in a group chat is visible to the direct chat session | openclaw-plugin covers all agent sessions; ~15 min propagation lag via auto_digest |
The Core Problem: Sessions Are Stateless โ
OpenClaw resets the active session daily (default 4:00 AM local time) or after an idle timeout. Every new session starts with a blank context window โ no memory of previous conversations.
This is by design: it keeps context fresh and avoids token bloat. But it means agents need an external mechanism to maintain continuity.
mem0 Memory Service is that mechanism.
How Agents Know What to Do: The Skill System โ
OpenClaw has a built-in skill system. When a skill is enabled, its SKILL.md file is automatically injected into the agent's system prompt at session start.
The mem0-memory skill's SKILL.md contains a ๐ด Agent Memory Behavior section with explicit behavior rules. When an agent reads these rules, it knows:
- When to write to the diary (
memory/YYYY-MM-DD.md) - When to update
MEMORY.md - How to search and write mem0 via CLI
This is why no AGENTS.md changes are needed. Enabling the skill once applies the behavior to every agent, on every session start, automatically.
Skills enabled in OpenClaw Settings
โ
โผ
SKILL.md injected into system prompt at session start
โ
โผ
Agent reads "๐ด Agent Memory Behavior" rules
โ
โโ During conversation โ write diary entries
โโ During heartbeat โ update MEMORY.mdAgent Behavior Rules (What the Skill Instructs) โ
During Conversations โ
When any of the following occurs, the agent proactively writes to memory/YYYY-MM-DD.md:
| Trigger | Example |
|---|---|
| Task completed | PR submitted, bug fixed, deployment done |
| Technical decision made | Why A was chosen over B |
| Lesson learned / pitfall discovered | Root cause of a failed build |
| User expressed a preference | "Always reply in Chinese" |
| Status changed | PR merged, service restarted |
Diary entry format:
## 14:30
- Fixed session_snapshot dedup bug: changed block-level comparison to line-level content matching
- PR #7 submitted and mergedDuring Heartbeats โ
On every heartbeat tick, the agent performs memory maintenance in order:
- Write diary โ append new session conversations to today's
memory/YYYY-MM-DD.md - Review recent files โ skim the last 2โ3 days for insights worth keeping long-term
- Update MEMORY.md โ distill durable facts into
MEMORY.md; prune outdated entries
MEMORY.md is the agent's curated knowledge base โ project cards, active decisions, key configurations. It's maintained by the agent itself, not by automation scripts.
The Full Memory Flow โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Conversation โ
โโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโ
โ โ
โผ โผ
Agent writes diary openclaw-plugin
(SKILL.md guided, agent_end hook
high quality) (real-time, every turn)
โ โ
โโโโโโโโโโโโฌโโโโโโโโโโโโ
โผ
memory/YYYY-MM-DD.md
(raw daily diary file)
โ
โโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโ
โ โ โ โ
โผ โผ โผ โผ
Heartbeat Every 15min UTC 02:00 UTC 01:00
(agent auto_digest auto_dream memory_sync
self- --today (Step1: (sync
distills) (infer=True, yesterday MEMORY.md)
direct text) diary +
Step2: 7d STM)
โ โ โ โ
โผ โผ โผ โผ
MEMORY.md mem0 short- mem0 long- mem0 long-
(updated) term memory term memory term memory
(today, (no run_id, (no run_id)
~15min lag) next day)
โ
UTC 02:00
AutoDream
โ
โโโโโโโโโโโดโโโโโโโโโโโ
โผ โผ
Step 1: digest Step 2: consolidate
yesterday diary 7-day-old short-term
โ long-term โ re-add long-term
(infer=True) (infer=True) + deleteDeployment: Docker vs. systemd โ
The memory pipeline scripts (auto_digest, auto_dream, memory_sync) run identically regardless of deployment method. Only the process manager differs:
| Docker (recommended) | systemd | |
|---|---|---|
| API server | mem0-api container | systemd service |
| Pipeline runner | mem0-pipeline container (cron) | systemd user timers |
| Diary file access | via bind mount ~/.openclaw โ /openclaw | direct filesystem |
| Patch management | automatic at build time | manual python3 tools/patch_s3vectors_filter.py |
| Restart on crash | restart: unless-stopped | systemd Restart=on-failure |
For deployment details, see Docker Setup or systemd Setup.
Daily Automation Timeline โ
| Time (UTC) | Script | What it does |
|---|---|---|
| Real-time | openclaw-plugin agent_end hook | Write conversation to diary file after each agent turn |
| Every 15 min | pipelines/auto_digest.py --today | Incremental: read new diary content โ mem0 short-term (infer=True, fact extraction) + task-extraction pass (custom_extraction_prompt โ category=task) |
| 01:00 | pipelines/memory_sync.py | Sync MEMORY.md โ mem0 long-term (hash dedup) |
| 02:00 | pipelines/auto_dream.py | AutoDream: Step 1: yesterday diary โ mem0 long-term (infer=True); Step 2: 7-day-old short-term โ re-add to long-term (infer=True) then delete |
In Docker deployments, the pipeline scripts run as cron jobs inside the
mem0-pipelinecontainer. In systemd deployments, they run as user timers. Diary files are written in real-time by the openclaw-pluginagent_endhook (outside Docker). The schedule and behavior of batch pipelines are identical regardless of deployment method.
auto_digest Mode โ
auto_digest.py runs in one active mode:
--today mode (every 15 min, incremental) Picks up new content from today's diary since the last run (offset-based). Writes to mem0 short-term memory with infer=True โ mem0 runs internal fact extraction to produce concise memories. Skips batches smaller than 500 bytes to avoid noisy micro-writes. On failure, retains the offset so the next run resumes from the same point.
Every 15 min (--today): diary new content โ POST to mem0 (infer=True, fact extraction)Why short-term entries don't pollute long-term memory
auto_digest writes with a run_id = YYYY-MM-DD. This is the key that controls mem0's dedup scope: when infer=True is used, mem0 searches for similar memories only within the same run_id. Since auto_digest uses infer=True, mem0 runs fact extraction at write time. Dedup scope is bounded by run_id, so today's entries only interfere with same-day memories, not existing long-term memories (which have no run_id).
This means:
- Writing every 15 minutes is safe โ no risk of silently overwriting long-term knowledge
- Today's short-term memories accumulate and refine within their own namespace
Promotion to long-term: one global dedup at auto_dream time
The cross-run_id merge happens only during auto_dream (UTC 02:00). When 7-day-old short-term memories are re-added without run_id, mem0 searches the entire long-term store and decides ADD / UPDATE / DELETE / NONE. This is the one moment of global dedup โ keeping long-term memory compact and non-redundant.
Every 15 min โ short-term (run_id=today, dedup within same day only)
UTC 02:00 โ long-term (no run_id, global dedup across all history)Targeted Extraction Pass (category=task) โ
After each session block is written to short-term memory, auto_digest runs a second pass with a task-focused custom_extraction_prompt:
Session block
โโ Pass โ infer=True (default prompt) โ category=short_term (general facts)
โโ Pass โก infer=True + custom_extraction_prompt โ category=task (completed work tasks)Pass โก extracts only finalized work outcomes in [type] description format (e.g. [ๅผๅ] ๅฎ็ฐไบ X ๅ่ฝ๏ผPR #129 ๅทฒๅผ). This makes task recall precise: querying "ๆ่ฟๅฎๆ็ไปปๅก" returns clean task entries instead of scattered facts.
Extending to other categories: The same pattern applies to any category. Agents can pass a custom_extraction_prompt on any /memory/add call to extract decisions, config changes, or custom dimensions โ without affecting the global mem0 prompt.
Note: The previous default full mode (UTC 01:30, LLM-extract yesterday's diary โ mem0 short-term) has been superseded by
auto_dream.pyStep 1, which writes directly to long-term memory (no run_id) with higher quality.
Real-Time Diary Capture via openclaw-plugin โ
The openclaw-plugin's agent_end hook replaces the previous session_snapshot.py polling approach. It fires after each agent turn completes, writing the conversation to the agent's diary file in real-time.
Primary: Cross-Session Memory Bridge Agents reset daily. Without diary capture, conversations would be lost between sessions. The agent_end hook captures every conversation turn into diary files, which are then distilled into mem0 by auto_digest.py. When a new session starts, the agent retrieves relevant memories from mem0 โ restoring context seamlessly.
Secondary: Compaction Guard When a session's context window grows too large, OpenClaw compresses (compacts) the history. The agent_end hook ensures content is captured to disk after every turn โ before compaction can lose it. This is strictly better than the old 5-minute polling approach, which could miss up to 5 minutes of conversation.
Tertiary: Near-real-time cross-session memory sharing Each agent may have multiple concurrent sessions. The agent_end hook writes new conversation to the diary file after every turn. auto_digest.py --today then picks up new diary content every 15 minutes and writes it to mem0 short-term memory. Any other session of the same agent can search mem0 and retrieve that content within ~15 minutes โ no session restart required.
Writing Diary Files โ
Diary files are written through two complementary paths:
| Path | Who writes | Quality | When |
|---|---|---|---|
| Agent-driven | Agent itself (SKILL.md guided) | High โ curated, meaningful entries | Real-time, during conversation |
| Plugin-driven | openclaw-plugin agent_end hook | Good โ full conversation capture | Real-time, after each agent turn |
Both paths write to the same memory/YYYY-MM-DD.md file in the agent's workspace (~/.openclaw/workspace-{agentId}/memory/). The plugin filters noise (greetings, short exchanges) via isNoise and cleans content via cleanContent before writing.
The agent-driven path produces better memories. The plugin provides comprehensive automated capture as a complement.
Three Paths to Long-Term Memory โ
| Path | Source | Latency | When to use |
|---|---|---|---|
| memory_sync.py | MEMORY.md | Same day | Agent-curated knowledge, important decisions |
| auto_digest + AutoDream | Daily diary | 7 days | Recurring discussions, gradual context buildup |
| Explicit CLI write | Agent on-demand | Immediate | Time-sensitive facts, mid-conversation decisions |
# Explicit write to long-term memory (no --run = long-term)
python3 cli.py add \
--user boss --agent <your-agent-id> \
--text "Decided to use S3 Vectors as primary vector store" \
--metadata '{"category":"decision"}'Cross-Agent Memory Sharing โ
One of the most powerful features of the memory service is automatic cross-agent knowledge sharing via the shared user space.
How It Works โ
When any agent writes a memory with category=experience or category=procedural, the CLI automatically writes a copy to the shared knowledge pool (user_id=shared). No extra step is needed.
# This automatically goes to both the agent's personal space AND user_id=shared
python3 cli.py add \
--user boss --agent dev \
--text "kiro-cli: always use exec(pty=true, background=true, workdir=...). Never add & in command." \
--metadata '{"category":"procedural"}'Automatic Shared Pool Inclusion on Search โ
Every search (/memory/search and /memory/search_combined) automatically includes results from user_id=shared, regardless of which agent is searching. This is done transparently in the server layer โ callers don't need to make a separate request.
Agent A writes experience โ personal space + shared pool
โ
โผ
Agent B searches for related topic
โ gets Agent A's experience automatically
(result tagged memory_type="shared")This means:
- If one agent discovers a bug fix or correct workflow, all agents benefit immediately
- No need to manually propagate knowledge between agents
- Shared results are tagged with
"memory_type": "shared"so agents can distinguish them
Memory Category Reference โ
| category | Purpose | Shared? | Example |
|---|---|---|---|
experience | Pitfalls discovered, bug fixes, incident post-mortems | โ Auto-shared | "PR #94 fixed search not including shared pool โ root cause was missing _search_shared()" |
procedural | Step-by-step how-to guides, correct tool usage patterns | โ Auto-shared | "kiro-cli: exec(pty=true, background=true, workdir=...) โ never add & in command" |
project | Project status, progress | โ Agent-specific | "mem0-memory-service port 8230, systemd mem0-memory.service" |
decision | Technical decisions and rationale | โ Agent-specific | "Chose pgvector over OpenSearch for local dev: lower cost, simpler setup" |
environment | Service addresses, configs, paths | โ Agent-specific | "EC2 us-east-1, AWS Bedrock for LLM and embedding" |
preference | User habits and communication preferences | โ Agent-specific | "Communicate in Chinese, concise and direct" |
short_term | Today's discussions, temporary task notes | โ Agent-specific | "Today discussed issue #93 token optimization" |
experiencevsprocedural:experience= "what happened + how it was resolved" (incident-driven, post-mortem style).procedural= "how to do X correctly" (reusable step-by-step guidance, how-to style). When in doubt: post-mortem โexperience; how-to guide โprocedural.
Search Ranking โ
Score Normalization โ
Different vector stores return scores with different semantics:
| Vector Store | Raw score meaning | Normalized |
|---|---|---|
| OpenSearch | Similarity (higher = more similar) | Pass-through |
| pgvector | Cosine distance (lower = more similar) | 1 - distance |
| S3 Vectors | Cosine distance (lower = more similar) | 1 - distance |
The service automatically normalizes all scores to a unified similarity scale [0, 1] (higher = more similar) before applying min_score filtering, time-decay blending, and returning results. This ensures consistent behavior regardless of which vector store backend is configured.
Time-Decay Weighting โ
By default, search results are ranked by a blend of vector similarity and time freshness. This prevents older, potentially outdated memories from ranking above more recent, relevant ones.
The Formula โ
final_score = 0.7 ร vector_similarity + 0.3 ร time_decay_weight
time_decay_weight = 0.5 ^ (age_days / 30)
# A 30-day-old memory gets ~0.5x time weight
# A 7-day-old memory gets ~0.85x time weight
# A 1-day-old memory gets ~0.98x time weightWhat Changes in the Response โ
When time-decay is applied, each result includes an original_score field (the raw vector similarity) alongside the final blended score:
{
"id": "...",
"memory": "kiro-cli: never add & in command",
"score": 0.87,
"original_score": 0.83,
"created_at": "2026-04-06T09:00:00Z"
}Disabling Time-Decay โ
Pass time_decay: false in the request body if you want pure vector similarity ranking (e.g., when searching historical records where recency doesn't matter):
curl -X POST http://localhost:8230/memory/search \
-H "Content-Type: application/json" \
-d '{"query": "...", "user_id": "boss", "agent_id": "dev", "time_decay": false}'Session Start: Restoring Context โ
Why today's AND yesterday's diary files are both needed โ
When a new session starts, the skill instructs the agent to read both diary files before querying mem0:
Session start
โโโ Read memory/today.md โ today's raw diary (real-time, written by openclaw-plugin)
โโโ Read memory/yesterday.md โ yesterday's raw diary (coverage gap buffer)
โโโ Search mem0 --combined โ distilled memories (long-term + recent short-term)Why today's diary?auto_dream.py Step 1 runs at UTC 02:00, digesting yesterday's complete diary. Anything that happened today hasn't been digested yet โ it only exists in memory/today.md. Reading this file is the only way to recover same-day context after a session reset.
Why yesterday's diary? There is a coverage gap: the window between yesterday's late-night conversations and when auto_dream finishes running (UTC 02:00). For example:
Yesterday 23:50 Important discussion happens โ written to memory/yesterday.md
Today 00:10 Session resets, new session starts
Today 02:00 auto_dream runs โ yesterday's diary enters mem0 long-termIf the new session starts before 02:00, mem0 doesn't have last night's content yet. Reading memory/yesterday.md directly covers this gap.
The complete coverage map:
| Time window | Covered by |
|---|---|
| Today (T+0) | memory/today.md (real-time) |
| Yesterday after-midnight to 02:00 | memory/yesterday.md (gap buffer) |
| Yesterday 02:00 onward | mem0 long-term (AutoDream Step1 digested) |
| Last 7 days | mem0 short-term (--combined) |
| Older than 7 days | mem0 long-term (AutoDream-consolidated) |
All three sources together โ today's diary + yesterday's diary + mem0 โ create zero blind spots across all session reset scenarios.
mem0 retrieval โ
# Combined search: long-term + recent 7 days short-term
python3 cli.py search \
--user boss --agent <your-agent-id> \
--query "<topic from current conversation>" \
--combined --recent-days 7This gives the agent:
- Recent discussions (from short-term, last 7 days)
- Durable knowledge (from long-term: decisions, lessons, preferences)
- Shared team knowledge (from
category=experiencepool, all agents)
The result: the agent "remembers" relevant context without the user needing to re-explain anything.
Finding Your Agent ID โ
All CLI commands use --agent <your-agent-id>. Your agent ID is the key under agents.entries in openclaw.json:
cat ~/.openclaw/openclaw.json | python3 -c "
import json, sys
config = json.load(sys.stdin)
entries = config.get('agents', {}).get('entries', {})
for agent_id, cfg in entries.items():
ws = cfg.get('workspace', 'N/A')
print(f' {agent_id}: workspace={ws}')
"Example output:
main: workspace=/home/user/workspace-main
dev: workspace=/home/user/workspace-dev
blog: workspace=/home/user/workspace-blogUse the key (e.g. dev, main, blog) as your --agent value.
AutoDream: The Sleeping Brain โ
"Every night, while the agent sleeps, its memories are quietly being sorted, promoted, and pruned โ just like a human brain during REM sleep."
The memory pipeline has two stages inspired by how human memory consolidation works:
- Auto Digest (The Subconscious) โ runs every 15 minutes during the day, continuously absorbing new diary content (written in real-time by the openclaw-plugin
agent_endhook) into short-term memory via dedicatedDIGEST_EXTRACTION_PROMPT(technical context preservation) +TASK_EXTRACTION_PROMPT(task extraction) - Auto Dream (Deep Sleep) โ runs once at night (UTC 02:00), reflecting on cross-day patterns, promoting 7-day-old short-term memories into long-term, and pruning redundancy
Neither stage requires human intervention. The agent wakes up the next morning with a richer, more organized memory.
For the full design, step-by-step pipeline breakdown, and a real end-to-end example, see AutoDream: The Sleeping Brain.
Memory Dashboard โ
For a visual interface to browse, search, and manage memories, see Memory Dashboard.