On this page

On this page

Sessions and memory

Builtin memory engine

The builtin engine is the default memory backend. By default, it stores your memory index in a per-agent SQLite database and needs no extra dependencies to get started. It also supports PostgreSQL when the agent store selects that engine; see PostgreSQL search and requirements.

What it provides

  • Keyword search via SQLite FTS5 (BM25 scoring) or PostgreSQL native full text.
  • Vector search via embeddings from any supported provider.
  • Hybrid search that combines both for best results.
  • Ranking by relevance, recency, and write-time importance, using fixed rules.
  • Diversity-aware ordering with MMR enabled on hybrid results by default.
  • Trusted trigger recall for bounded pre-reply context without a recall model.
  • CJK support via trigram tokenization for Chinese, Japanese, and Korean.
  • sqlite-vec acceleration for in-database vector queries (optional).

Native sqlite-vec queries run in a separate, read-only process so a slow query does not block the Gateway event loop. Cancelling a search terminates its query process; OpenClaw does not retry that native query on the Gateway thread. Queries reuse a process for each database, with at most two processes alive. Idle processes retire after 30 minutes or when another database needs capacity. Each query reopens the database so committed updates and replaced indexes remain visible.

Keyword retrieval, recall metadata, curated trigger and project candidates, and source timestamps use the memory search worker. The Gateway awaits projected rows and applies the same ranking. Session-only searches retain their final metadata and timestamp reads on the caller because an additional worker request increased measured latency; other retrieval reads run off the Gateway event loop. Searches retain their index generation until the worker closes its reader; recall metadata is read after candidate retrieval so forgotten chunks are excluded. This does not change stored data, configuration, or upgrade behavior.

After Gateway readiness, idle warmup loads the active Memory Core retrieval worker before the first search. It does not open an index, start an embedding provider, or delay readiness. Requests arriving before warmup completes still initialize retrieval normally; the worker keeps its existing idle retirement policy.

If semantic retrieval reaches the 30-second tool deadline after keyword matches from memory files are ready, memory_search returns those matches with a partial-result warning. Session transcript hits require fresh visibility checks and are excluded from timeout recovery. A partial response does not put the entire memory corpus into the timeout cooldown. When the agent provides no final reply, the fallback warning states the timeout duration and whether partial results are available.

When to use

The builtin engine is the right choice for most users:

  • Works out of the box with no extra dependencies.
  • Handles keyword and vector search well.
  • Supports all embedding providers.
  • Hybrid search combines the best of both retrieval approaches.

The builtin engine can index directories outside the workspace with memory.search.extraPaths. It uses bounded lexical query expansion to improve conversational recall, but it does not provide a learned or model-based relevance reranking stage. Its MMR pass uses fixed rules and runs locally.

Consider Honcho if you want cross-session memory with automatic user modeling.

Getting started

By default, the builtin engine uses OpenAI embeddings. If OPENAI_API_KEY or models.providers.openai.apiKey is already configured, vector search works with no extra memory config.

To set a provider explicitly:

json5
{  memory: {    search: {      provider: "openai",    },  },}

Without an embedding provider, only keyword search is available.

To force local GGUF embeddings, install and configure the official llama.cpp provider, then point local.modelPath at a GGUF file:

bash
openclaw plugins install @openclaw/llama-cpp-provider
json5
{  memory: {    search: {      provider: "local",      fallback: "none",      local: {        modelPath: "~/.openclaw/models/llama.cpp/hf_ggml-org_embeddinggemma-300m-qat-Q8_0.gguf",      },    },  },}

Supported embedding providers

Provider ID Notes
Bedrock bedrock Uses the AWS credential chain
DeepInfra deepinfra Default: BAAI/bge-m3
Gemini gemini Supports multimodal (image + audio)
GitHub Copilot github-copilot Uses your Copilot subscription
LM Studio lmstudio Local/self-hosted
Local local OpenClaw-managed llama.cpp server
Mistral mistral
Ollama ollama Local/self-hosted
OpenAI openai Default: text-embedding-3-small
OpenAI-compatible openai-compatible Generic /v1/embeddings endpoint
Voyage voyage

Set memory.search.provider to switch away from OpenAI.

How indexing works

OpenClaw indexes MEMORY.md, an existing root USER.md, and memory/*.md into chunks (400 tokens with 80-token overlap by default) and stores them in a per-agent database. OpenClaw does not create USER.md automatically.

Each chunk can carry nullable importance and trigger metadata. Null values are neutral, so older indexes remain usable. Search combines hybrid relevance, recency decay, and importance before applying MMR diversity; trigger recall only injects curated or promoted-trusted entries.

Each indexed chunk also has database-owned provenance: origin class (owner, agent, untrusted, or system), session kind, observation time, and an optional supersession key. This metadata is stored separately from Markdown so recalled prose cannot rewrite its own trust classification. Automatic session ingestion also records source-session origins for its staged entries, which support selective deletion after promotion. For coverage and limits, see Memory provenance and deletion.

  • Index location: the owning agent database; SQLite uses ~/.openclaw/agents/<agentId>/agent/openclaw-agent.sqlite, while PostgreSQL keeps a local anchor at that path and stores the index on the server.
  • SQLite storage maintenance: WAL sidecars are bounded with periodic and shutdown checkpoints.
  • File watching: changes to memory files trigger a debounced reindex (1.5s default).
  • Index compatibility: changing the embedding provider, model, settings, configured sources, or scope can pause search until you explicitly rebuild. See provider selection.
  • Reindex on demand: openclaw memory index --force --agent <id>

When Memory Core owns the memory slot, a fresh Gateway start opens an agent's memory manager on its first memory search, flush, or turn that needs it, so an idle Gateway does not start index and transcript workers for every agent. Once a manager has run in the Gateway process, the Memory Core service schedules each configured agent's memory manager to open again whenever the service restarts, including after plugin replacement and memory or agent configuration changes. This background activation waits for that agent's database preparation, including after an upgrade or restart, without delaying Gateway readiness. Failed preparation reports the database's repair guidance. Watched file changes can then update the index without a search or agent turn. Retiring an instance closes its managers, including file watchers, timers, and session listeners. A session startup scan still in progress stops after its current step, so disabling or replacing the plugin does not wait for it. Plugin reload also stops and restarts the retained Memory Core service around publication, so its managers use the current embedding providers, including providers loaded on demand, without waiting for a search or turn. If reload fails after draining managers, recovery restarts their previous services and schedules their index activation before reporting the previous runtime restored. Service stop joins pending activation before closing its managers. Memory Core running only as another memory plugin's consolidation sidecar does not start these indexes automatically, and it does not register memory_search or memory_get; the selected memory plugin owns recall.

When the index identity reports an OpenClaw chunking-implementation change, a normal or CLI search rebuilds it before returning results. The rebuild uses the agent's current embedding settings; status inspection remains read-only.

Resident searches read the published index without starting source-wide reconciliation. Pending updates remain visible as a stale-result warning. File watchers apply changes incrementally. Session transcript indexing waits until the agent has had no transcript changes or running turns for 30 seconds. While the agent is active, pending changes keep their original age. After about five minutes, indexing starts in the next gap between turns without waiting for another quiet period. Ordinary session passes wait for a gap between turns. One pass covers the agent's pending sessions, sharing transcript preparation, embedding-cache reads, and bounded publication transactions. If a turn starts during a pass, indexing finishes its current bounded group and resumes after the next quiet period. The same indexing timer also checks for missed file and transcript events every five minutes, using the existing maximum-deferral interval. This recovery pass runs even during an active turn and keeps the published index available to searches. The deadline bounds when recovery starts; source size, embedding failures, and full-rebuild backoff can delay completion. Standalone openclaw memory search refreshes sources before returning because it has no resident maintenance timer. Repeated notifications for an unchanged transcript do not rewrite its source metadata; changes to its content, timestamp, or size still update the index.

Failed session passes, including handled embedding-provider fallback failures, retain their pending work and retry after 1, 5, and 15 minutes, subject to the same idle gate. The last automatic retry also waits for any full-rebuild cooldown to end. After those automatic retries, another transcript change or turn activity can start a new retry series. A running turn can extend either delay.

New session content becomes searchable after that delay. The current turn already has its own content in context; memory_search does not force a transcript flush. Graceful shutdown does not flush queued indexing. On the next startup, incremental catch-up resumes from each source's indexed sequence, including rewrites and compaction. Startup compares source metadata; missing transcripts are parsed and checked for indexing eligibility once the background pass can run. Explicit source diagnostics still inspect content to exclude system-only transcripts from eligible counts. A failed full rebuild retains its full-retry state; ordinary dirty content does not itself force a rebuild. An index built before any content exists remains valid as content arrives; restarting does not require a full rebuild just because the index started empty. Growing session transcripts and edited memory files retain unchanged indexed chunks and embed only new or changed chunks, even when the embedding cache is disabled. An edit that changes every chunk of a memory file, such as a line inserted at the top, still replaces that file's chunks. An explicit full rebuild still replaces the complete index. If a memory file changes or disappears during indexing, only that file's unfinished work is retried incrementally. Other files finish indexing, and the changed file's obsolete chunks are not published.

Automatic full-rebuild retries wait 30 seconds after a failure. Repeated failures double that delay up to 30 minutes; a successful rebuild resets it. Searches keep reading the published index, and targeted session updates can still publish when its identity is valid. Explicit CLI repair bypasses the process-local cooldown.

Optional embedding-provider failures also share their 30-second recovery cooldown with background maintenance. Searches retain keyword recall, and existing semantic indexes remain intact. An already keyword-only index can still accept file updates during the cooldown; semantic indexing resumes after recovery.

Local file observation runs in one shared file worker, including initial directory discovery, native watching, and change settling. Large memory trees therefore do not require directory traversal on the Gateway's main thread. Indexing stays with the memory manager, which receives dirty notifications before debounce and reads published watcher health without polling the worker. Startup waits for watcher readiness; shutdown joins native cleanup, and the last watcher closes the worker.

If the shared file worker exits unexpectedly, Memory Core reports unavailable watcher health and logs a warning. It restarts after 0.5, 2, and 5 seconds, restoring live subscriptions and rescanning their scopes to catch missed edits. Four failures within one minute stop automatic restarts; health remains unavailable and the indexing timer continues to reconcile memory. Restart the Gateway after resolving the worker failure to resume observation. Normal shutdown does not restart the worker.

When native file watching is unavailable, Memory Core uses background polling with a 30-second default interval, including when polling is explicitly enabled with CHOKIDAR_USEPOLLING. A valid CHOKIDAR_INTERVAL overrides this default, with a 20 ms minimum. Shorter intervals increase background scanning cost, especially for large memory trees. Native events still trigger prompt, debounced updates. Native watchers also perform a full recovery scan every five minutes, so a missed native event can take that long to be detected. This recovery interval does not change polling cadence. Automatic fallback logs one warning per watcher lifetime. Agents in the same process share observations of the same admitted root, retaining each agent's file-selection rules. Joining or leaving an unchanged selection keeps the existing filesystem baseline; changes to the combined scopes or enabled file kinds rescan it. More than 128 combined scopes use additional bounded subscriptions. If a shared observation fails, its members retry independently for the rest of their watcher lifetimes, so one failing scope cannot repeatedly disable healthy peers. The last agent to stop observing a root closes its subscription. A running memory manager exposes each local observation's mode, polling interval, and pollingFallback in its status under custom.watcher; standalone status inspection does not start a watcher. The filesystem library does not currently retain the fallback reason or retry native selection; the warning says when no reason was reported. Restart the Gateway after resolving the native backend problem to try native watching again.

If the host runs out of native file-watch capacity, Memory Core logs one warning and disables its watchers. The indexing timer continues incremental synchronization to discover file changes at its five-minute recovery interval. Searches use the previous index while that work finishes and report pending updates. Restart the Gateway after restoring watch capacity to enable native watching again.

Incremental indexing, stale-source cleanup, and cache pruning wait asynchronously when another SQLite writer is active. Cache pruning removes entries in bounded batches, in the order described below, yielding between batches while preserving the existing cache cap.

Full reindexes build a replacement in a temporary database and publish the memory tables atomically. Concurrent searches and status reads keep using the published index; a failed rebuild leaves that index intact. The embedding cache can miss vectors that are still in the published index. With embedding caching enabled, full rebuilds reuse those vectors in bounded source batches when provider, model, provider settings, input format, chunk hash, and dimensions match. Invalid vectors are embedded again. The embedding cache may temporarily hold up to twice its configured row cap during a full rebuild, so new embeddings do not evict cached vectors that later batches still need. It is pruned to the configured cap after publication or abort. A suspended background rebuild keeps that temporary budget until it resumes or is discarded. A process crash can leave the temporary excess until the next cache write enforces its capacity budget. Incremental writes keep their normal pre-insertion cap. When entries must be evicted, the cache first takes entries for the active embedding provider that no published chunk references and that were written before the oldest published chunk, oldest first. A full reindex rewrites every chunk, so these entries predate the index that dropped them. Incremental syncs keep unchanged chunks, so the oldest published chunk can be much older and fewer entries qualify. Newer unreferenced entries, such as a retry's or another sync's unpublished vectors, keep plain oldest-first order. At or below the cap nothing is removed.

Other agent state, including sessions and transcripts in the same database, is retained. Use the memory index command for memory-only repair.

openclaw memory status reports stored chunk text and binary embedding bytes for each source (sourceCounts[].chunkBytes in JSON). These are payload sizes, not total disk usage: embedding cache, FTS/vector tables, SQLite overhead, and WAL/free pages are excluded.

Chunk and embedding-cache vectors use little-endian 64-bit floating-point BLOBs. The software search fallback scores these full-precision BLOBs directly in a worker, without allocating a numeric array per chunk, even when the optional sqlite-vec accelerator is unavailable; sqlite-vec keeps its separate 32-bit vector index. The keyword index uses each chunk's stable integer identity, so edits and deletion update the corresponding SQLite FTS rows directly.

On PostgreSQL, exact path matches use three B-tree indexes of bounded digests for normalized paths, basenames, and extensionless basenames. The original path predicate rechecks candidates, preserving search results and ranking while avoiding a full source scan for each keyword query. Agent schema 36 installs these indexes through Doctor; SQLite keeps its existing keyword search, including the broader scan used by Unicode fallback queries.

Agent schema 23 converts existing JSON vectors locally, without contacting an embedding provider. It preserves chunk IDs, provenance, recall metadata, and cache identities. Malformed legacy vectors retain their searchable text and mark their sources for reindexing. Unknown schema extensions that cannot be preserved cause migration to stop without rewriting those tables. Follow the database versioning and rollback contract when upgrading or returning to an older build.

After an upgrade, automatic project and trigger recall may need to repair legacy provenance. That repair runs in the background. Replies continue while automatic recall stays empty until the affected sources have been reclassified.

Migrating from QMD

QMD has been removed; builtin is the only memory engine. After upgrading, run:

bash
openclaw doctor --fix

Doctor removes the retired memory.backend, memory.qmd, and memory.search.qmd settings, including agent-scoped memory.search.qmd forms. It preserves QMD paths and extra collections as the corresponding memory.search.extraPaths entries, including { path, pattern } globs. When QMD session indexing was enabled, Doctor also enables builtin session indexing and adds sessions to memory.search.sources without enabling broader cross-conversation recall. Retained session-reset transcripts remain in the agent's sessions directory and are indexed from those original artifacts.

Doctor removes only empty per-agent QMD directories under ~/.openclaw/agents/<agentId>/qmd/. Nonempty directories stay untouched: OpenClaw's retired QMD backend used the same layout as standalone QMD, without an ownership marker. Retained directories do not block migration or Gateway startup. After backing them up, you can remove old indexes, model downloads, collection metadata, and session exports manually if you have confirmed that no standalone QMD installation uses them.

The original memory remains in MEMORY.md, USER.md, memory/*.md, and the migrated extra paths. Builtin indexes those same Markdown sources on its next sync. The cutover is lossless by construction: no original memory content is copied or deleted; only derived state is rebuilt.

Builtin now covers most QMD use cases with:

  • hybrid BM25 and vector retrieval by default, followed by temporal decay, importance, and project affinity before MMR diversity,
  • bounded lexical query expansion for conversational searches,
  • string or { path, pattern, evergreen } entries in memory.search.extraPaths, and
  • optional image and audio indexing under extraPaths only.

QMD query mode's learned cross-encoder reranking and HyDE generation are not part of builtin memory. MMR reduces duplicate results but is not a learned relevance reranker. To replace QMD's in-process, zero-key GGUF embeddings, install the llama.cpp provider and set memory.search.provider: "local"; without an embedding provider, builtin uses BM25 keyword search only.

Troubleshooting

Memory search disabled? Check openclaw memory status. If no provider is detected, set one explicitly or add an API key.

Local provider not detected? Run the interactive llama.cpp setup once with openclaw onboard, confirm the local path exists, and run:

bash
openclaw memory status --deep --agent mainopenclaw memory index --force --agent main

Both standalone CLI commands and the Gateway use the same local provider id. Set memory.search.provider: "local" when you want local embeddings.

Stale results? Run openclaw memory index --force to rebuild. The watcher may miss changes in rare edge cases.

sqlite-vec not loading? OpenClaw falls back to in-process cosine similarity automatically. openclaw memory status --deep reports the local vector store separately from the embedding provider, so Vector store: unavailable points at sqlite-vec loading while Embeddings: unavailable points at provider/auth or model readiness. Check logs for the specific load error.

Safe index recovery

To rebuild after stale results or an embedding-provider change, select the affected agent explicitly:

bash
openclaw memory status --agent <agent-id> --deepopenclaw memory index --agent <agent-id> --force --verboseopenclaw memory status --agent <agent-id> --deep

To discard the derived index and embedding cache before rebuilding, use memory reset:

bash
openclaw memory reset --agent <agent-id>openclaw memory index --agent <agent-id>

Reset asks for confirmation; add --yes for non-interactive use. It clears only memory-owned derived tables, preserving non-memory database tables, including sessions and transcripts, and memory source files. It coordinates with existing memory maintenance without restarting the Gateway, which can reindex retained sources afterward. If indexing is busy, let it finish and retry reset. Reset does not shrink the database file or recover already deleted data.

If indexing fails or the database grows unexpectedly, keep the database and its sidecars, retain the verbose error, and create and verify a backup before manual recovery. A large database alone does not show which tables are responsible. Reindexing is not a session-history restore: if history is missing after moving or deleting the database, recover from a verified backup using the restore workflow.

Reclaim disk space

Start with openclaw memory status --agent <agent-id> --json. Compare the database and WAL sizes, reusable bytes, retained embedding-cache payload, and per-source chunk payloads. Reusable bytes are pages already free inside SQLite; they are not additional data. Cache and chunk payloads exclude indexes and SQLite overhead, so they do not explain every byte in the shared file.

If the derived index needs to be discarded, create and verify a backup, then stop the Gateway through its deployment owner and stop other writers. Keep them stopped through reset and compaction so background indexing cannot refill the cache between commands:

bash
openclaw memory reset --agent <agent-id> --yesopenclaw doctor --session-sqlite compact --session-sqlite-agent <agent-id>openclaw memory index --agent <agent-id>openclaw memory status --agent <agent-id>

If only unused pages need reclaiming, skip reset and preserve the existing index. Doctor compacts the whole agent database, verifies integrity, and reports the before/after database and WAL sizes. Compaction needs temporary disk space; on a full volume, free space or move a verified backup to a volume with sufficient capacity before attempting it. Rebuilding can call the embedding provider and incur cost. Restart the Gateway through its deployment owner after verification. Neither reset nor compaction removes stored sessions or changes retention.

Configuration

For embedding provider setup, search result limits and thresholds, batch indexing, multimodal memory, sqlite-vec, extra paths, and all other config knobs, see the Memory configuration reference.

Was this useful?