Skip to content

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 PrincipleWhat It MeansKey Mechanism
Zero blind spots across session resetsNo conversation is lost when a session ends, even mid-dayToday's diary + yesterday's diary + mem0 combined cover every time window
Two-tier memory: short-term โ†’ long-termRecent content stays isolated; only proven facts graduate to long-termauto_digest writes with run_id=today (bounded dedup); auto_dream promotes at UTC 02:00 (global dedup)
Real-time diary captureEvery conversation turn is captured immediatelyopenclaw-plugin agent_end hook writes diary in real-time; no polling delay
Semantic retrieval, not keyword searchFind memories by meaning, not exact phrasingVector similarity + time-decay blended ranking (0.7 ร— score + 0.3 ร— recency)
Cross-agent knowledge sharingLessons learned by one agent instantly benefit all agentscategory=experience / procedural auto-writes to shared pool; every search includes shared pool
Write generously, dedup automaticallyAgents don't need to worry about redundancy โ€” mem0 handles itinfer=True triggers internal fact extraction; same-day dedup is bounded by run_id
Targeted extraction per categoryDifferent memory types need different extraction anglescustom_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 memoryDifferent sources land in long-term at different speedsExplicit CLI write (immediate) โ†’ memory_sync (same day) โ†’ auto_dream (7-day cadence)
Multi-session, multi-agent continuityWhat happens in a group chat is visible to the direct chat sessionopenclaw-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.md

Agent Behavior Rules (What the Skill Instructs) โ€‹

During Conversations โ€‹

When any of the following occurs, the agent proactively writes to memory/YYYY-MM-DD.md:

TriggerExample
Task completedPR submitted, bug fixed, deployment done
Technical decision madeWhy A was chosen over B
Lesson learned / pitfall discoveredRoot cause of a failed build
User expressed a preference"Always reply in Chinese"
Status changedPR merged, service restarted

Diary entry format:

markdown
## 14:30

- Fixed session_snapshot dedup bug: changed block-level comparison to line-level content matching
- PR #7 submitted and merged

During Heartbeats โ€‹

On every heartbeat tick, the agent performs memory maintenance in order:

  1. Write diary โ€” append new session conversations to today's memory/YYYY-MM-DD.md
  2. Review recent files โ€” skim the last 2โ€“3 days for insights worth keeping long-term
  3. 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) + delete

Deployment: 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 servermem0-api containersystemd service
Pipeline runnermem0-pipeline container (cron)systemd user timers
Diary file accessvia bind mount ~/.openclaw โ†’ /openclawdirect filesystem
Patch managementautomatic at build timemanual python3 tools/patch_s3vectors_filter.py
Restart on crashrestart: unless-stoppedsystemd Restart=on-failure

For deployment details, see Docker Setup or systemd Setup.

Daily Automation Timeline โ€‹

Time (UTC)ScriptWhat it does
Real-timeopenclaw-plugin agent_end hookWrite conversation to diary file after each agent turn
Every 15 minpipelines/auto_digest.py --todayIncremental: read new diary content โ†’ mem0 short-term (infer=True, fact extraction) + task-extraction pass (custom_extraction_prompt โ†’ category=task)
01:00pipelines/memory_sync.pySync MEMORY.md โ†’ mem0 long-term (hash dedup)
02:00pipelines/auto_dream.pyAutoDream: 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-pipeline container. In systemd deployments, they run as user timers. Diary files are written in real-time by the openclaw-plugin agent_end hook (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.py Step 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:

PathWho writesQualityWhen
Agent-drivenAgent itself (SKILL.md guided)High โ€” curated, meaningful entriesReal-time, during conversation
Plugin-drivenopenclaw-plugin agent_end hookGood โ€” full conversation captureReal-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 โ€‹

PathSourceLatencyWhen to use
memory_sync.pyMEMORY.mdSame dayAgent-curated knowledge, important decisions
auto_digest + AutoDreamDaily diary7 daysRecurring discussions, gradual context buildup
Explicit CLI writeAgent on-demandImmediateTime-sensitive facts, mid-conversation decisions
bash
# 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.

bash
# 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"}'

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 โ€‹

categoryPurposeShared?Example
experiencePitfalls discovered, bug fixes, incident post-mortemsโœ… Auto-shared"PR #94 fixed search not including shared pool โ€” root cause was missing _search_shared()"
proceduralStep-by-step how-to guides, correct tool usage patternsโœ… Auto-shared"kiro-cli: exec(pty=true, background=true, workdir=...) โ€” never add & in command"
projectProject status, progressโŒ Agent-specific"mem0-memory-service port 8230, systemd mem0-memory.service"
decisionTechnical decisions and rationaleโŒ Agent-specific"Chose pgvector over OpenSearch for local dev: lower cost, simpler setup"
environmentService addresses, configs, pathsโŒ Agent-specific"EC2 us-east-1, AWS Bedrock for LLM and embedding"
preferenceUser habits and communication preferencesโŒ Agent-specific"Communicate in Chinese, concise and direct"
short_termToday's discussions, temporary task notesโŒ Agent-specific"Today discussed issue #93 token optimization"

experience vs procedural: 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 StoreRaw score meaningNormalized
OpenSearchSimilarity (higher = more similar)Pass-through
pgvectorCosine distance (lower = more similar)1 - distance
S3 VectorsCosine 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 weight

What 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:

json
{
  "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):

bash
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-term

If 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 windowCovered by
Today (T+0)memory/today.md (real-time)
Yesterday after-midnight to 02:00memory/yesterday.md (gap buffer)
Yesterday 02:00 onwardmem0 long-term (AutoDream Step1 digested)
Last 7 daysmem0 short-term (--combined)
Older than 7 daysmem0 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 โ€‹

bash
# 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 7

This gives the agent:

  • Recent discussions (from short-term, last 7 days)
  • Durable knowledge (from long-term: decisions, lessons, preferences)
  • Shared team knowledge (from category=experience pool, 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:

bash
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-blog

Use 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_end hook) into short-term memory via dedicated DIGEST_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.

Released under the MIT License.