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:
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:
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:
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 inmemory.search.extraPaths, and - optional image and audio indexing under
extraPathsonly.
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:
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:
To discard the derived index and embedding cache before rebuilding, use
memory reset:
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:
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.