OpenClaw long-term memory plugin backed by your own self-hosted SynapCores AIDB gateway. Passive by default (explicit memory_store/recall/forget tools); an OPT-IN lifecycle, off by default, can proactively capture conversation-derived personal facts at the end of each agent turn and before context compaction. Data stays in your gateway — no third-party egress.
Install
openclaw plugins install clawhub:@synapcores/openclaw-memory@synapcores/openclaw-memory
A long-term memory plugin for OpenClaw that uses a SynapCores AIDB gateway you host as the storage backend. Drop-in alternative to @openclaw/memory-lancedb, plus four SynapCores-only extensions: SQL-filtered semantic recall, graph-relation walks, AutoML relevance scoring, and a model-training helper.
What it does with your conversations (read this first). By default the plugin is passive: it stores or recalls memory only when you explicitly call a tool (
memory_store/memory_recall/memory_forget) or theltmcommand. It also offers an opt-in automatic lifecycle (autoCapture/autoRecall, both off by default). When you turnautoCaptureon, the plugin becomes proactive: at the end of every agent turn and again just before OpenClaw compacts the context, it inspects the conversation, infers durable personal facts, preferences, and decisions, and persists them (in the model's own words) to your SynapCores gateway — andautoRecallinjects matching memories back into the model's context on later turns. This is real long-term memory of personal conversation details; enable it deliberately. See Privacy & data handling.
0.5.0 shipping note: the core memory ops (
memory_store/memory_recall/memory_forget) ride the engine-sideMEMORY_STORE/MEMORY_RECALL/MEMORY_FORGETprimitives via@synapcores/sdk@^0.6.0'sclient.memorysurface. The plugin's public API (tools, CLI, extensions, types) is unchanged.
- Requires SynapCores gateway
v1.8.5-ceor newer (the version that ships theMEMORY_*SQL functions).- Fully engine-native embeddings — zero external-LLM dependency. The
memory_store/memory_recallhot path embeds server-side inside the engine'sMEMORY_STORE/MEMORY_RECALLprimitives; the relevance extensions (predictRelevance/trainRelevanceModel) and theautoLinkSimilargraph-node embedding call the gateway's nativeclient.embed(). All embeddings come from the SynapCores gateway (embedding dimension is the gateway model's, e.g. 384 forall-minilm). OpenAI has been removed entirely — no OpenAI key, noopenaidependency.- Migration from 0.3.x: the engine-managed table is
_memory_<namespace>, a different storage backend from the v0.3.x vector collection. Existing v0.3.x memories WILL NOT appear after upgrade — re-capture them. See "Upgrading from 0.3.x" below.collectionconfig field becomes the enginenamespace. It must now match^[A-Za-z_][A-Za-z0-9_]*$. The defaultopenclaw_memoriescontinues to work; other custom values with hyphens or other non-identifier characters need updating.recallFilteredWHERE clauses are applied client-side. The engine cannot apply aWHEREto the table-valuedMEMORY_RECALL(?, ?, ?)result-set (it drops every row), so the plugin fetches an oversampled, unfiltered recall and evaluates the predicate in JS. Legacy column shorthands (category,importance,createdAt,text) and the JSON-extract form (metadata->>'…') are both understood directly — no rewriting required.
Upgrading from 0.3.x
@synapcores/openclaw-memory@0.5.0 is a hard cut from 0.3.x: the storage backend changed (at 0.4.0), so old memories will not migrate automatically. Steps:
- Upgrade the SynapCores gateway to
v1.8.5-ceor newer. npm install @synapcores/openclaw-memory@0.5.0.- If your
collectionconfig value contains hyphens or other non-identifier characters, rename it to match^[A-Za-z_][A-Za-z0-9_]*$before restarting. - (Optional) export any high-value memories from the v0.3.x vector collection (the legacy
openclaw_memoriescollection in your gateway) and re-store them viamemory_storeso they land in the new_memory_<namespace>table. - (Optional) drop the old vector collection from the gateway once you're sure the export is done.
If your recallFiltered callers use plain column names (category, importance, createdAt, text), they continue to work — the client-side filter understands those column names directly. Callers filtering on arbitrary metadata keys use the JSON-extract form (metadata->>'…').
Why use this over @openclaw/memory-lancedb?
| Capability | memory-lancedb | memory-synapcores |
|---|---|---|
| Vector recall + capture | yes | yes |
| Auto-recall / auto-capture hooks | yes | yes |
| GDPR-style forget by ID or query | yes | yes |
SQL-scoped semantic recall (recallFiltered) | no | yes |
Graph relation walks (recallRelated) | no | yes |
AutoML relevance scoring (predictRelevance) | no | yes |
| Backend | local LanceDB files | SynapCores gateway (HTTP) |
If you only need a private, single-user, file-backed vector store, stay on @openclaw/memory-lancedb. If you want any of: cross-session/multi-device shared memory, SQL filtering across metadata, graph relations between memories, or per-user relevance models — install this package.
Install
pnpm add @synapcores/openclaw-memory
# or
npm install @synapcores/openclaw-memory
openclaw (the host) is declared as a peer dependency — install it in your OpenClaw workspace.
Prerequisites
You need a running SynapCores gateway. The Community Edition is free:
# Linux/macOS one-liner installer (see https://synapcores.com/install)
curl -fsSL https://synapcores.com/install.sh | sh
# Then start it:
synapcores start
Create an API key from the SynapCores admin UI (default http://localhost:8095) and copy it into your OpenClaw config below.
Configure
Requires OpenClaw >=2026.4.10. Install the plugin, then add its config and
give it the memory slot:
openclaw plugins install @synapcores/openclaw-memory
Add this to your OpenClaw config (run openclaw config file to find the path,
typically ~/.openclaw/openclaw.json). Three things matter: the
plugins.entries.<id>.config nesting, the plugins.allow entry, and
plugins.slots.memory:
{
"plugins": {
"allow": ["memory-synapcores"],
"slots": { "memory": "memory-synapcores" },
"entries": {
"memory-synapcores": {
"enabled": true,
"hooks": {
"allowConversationAccess": true
},
"config": {
"synapcores": {
"host": "localhost",
"port": 8080,
"apiKey": "${SYNAPCORES_API_KEY}",
"useHttps": false
},
"collection": "openclaw_memories",
"graph": "openclaw_memory_graph",
"autoCapture": false,
"autoRecall": false,
"autoLinkSimilar": false
}
}
}
}
}
You must set
plugins.slots.memoryto"memory-synapcores". Only one plugin can own the memory slot, and the default is OpenClaw's built-inmemory-core— without claiming the slot the plugin loads but stays disabled.
You must set
plugins.entries.memory-synapcores.hooks.allowConversationAccesstotrue. OpenClaw gates conversation-lifecycle hooks (before_agent_start,agent_end) behind this flag for any non-bundled plugin. Without it, the plugin loads and its tools work, butautoCapture/autoRecallsilently do nothing — the gateway logstyped hook "agent_end" blocked because non-bundled plugins must set ...hooks.allowConversationAccess=trueand moves on. This lives outsideconfigSchema(it's an OpenClaw host-level permission, not a plugin config field), so it won't show up inopenclaw config validateerrors — check the gateway log if auto-capture seems inactive.
Then openclaw config validate. Environment-variable interpolation
(${SYNAPCORES_API_KEY}) is supported in any string field
so you don't have to commit secrets. (Store keys clean — a trailing newline
in apiKey will break auth.)
Privacy & data handling
This plugin is privacy-safe by default and passive out of the box.
- Automatic capture and recall are opt-in.
autoCapture,autoRecall, andautoLinkSimilarall default tofalse. With the defaults, the plugin never reads or writes memory on its own — it stores or recalls only when you explicitly invoke a tool (memory_store,memory_recall,memory_forget) or theltmcommand. - What "opt-in automatic capture" actually does. When you set
autoCapture: true, the plugin subscribes to theagent_endconversation hook and, at the end of every agent turn, inspects that turn and stores the durable personal facts, preferences, and decisions it infers — paraphrased in the model's own words, not raw transcripts — to your gateway. Separately, the pre-compaction flush fires just before OpenClaw discards older context and prompts the agent to persist anything still worth keeping. Both require the host permissionplugins.entries.memory-synapcores.hooks.allowConversationAccess: true.autoRecall: truethen injects matching stored memories back into the model's context on later turns. Turn these on only where proactive persistence of personal conversation details is acceptable. - Pre-compaction flush is part of that opt-in. The plugin only registers a
pre-compaction memory-flush capability when
autoCaptureis enabled; with the default off, the agent is never prompted to store facts automatically. - You control your data. Review memories with
memory_recall/ltm, and delete them by id or query withmemory_forget(GDPR-style forget). - No third-party egress or telemetry. The plugin talks to exactly one
endpoint — the SynapCores gateway you configure (
synapcores.host/apiKey). It sends no analytics, usage data, or conversation content anywhere else. Your memories live in your own self-hosted database. - Prefer HTTPS + a controlled gateway. Set
synapcores.useHttps: truefor any non-localhost gateway so memory content is encrypted in transit. - Don't store secrets or regulated data in shared or sensitive workspaces, and confirm you can review/delete memories before enabling auto-capture there.
Config fields
| Field | Required | Default | Notes |
|---|---|---|---|
synapcores.apiKey | yes | — | SynapCores API key (ak_prod_… or aidb_…). |
synapcores.host | no | localhost | SynapCores gateway hostname. |
synapcores.port | no | 8080 | SynapCores gateway port. |
synapcores.useHttps | no | false | Use TLS to talk to the gateway. |
collection | no | openclaw_memories | SynapCores collection name. |
graph | no | openclaw_memory_graph | SynapCores graph name (used for SIMILAR_TO edges and recallRelated walks). |
autoCapture | no | true | Auto-store memorable utterances after each agent turn. |
autoRecall | no | true | Auto-inject relevant memories before each agent turn. |
autoLinkSimilar | no | true | On capture, insert each Memory as a graph node carrying the embedding so recallRelated returns useful neighborhoods out of the box. Adds ~30-50ms per capture; disable if you never call recallRelated. |
workspace | no | — | Optional workspace suffix on the AutoML relevance model name so multiple installations sharing one gateway can train independent models. |
What you get
Once registered, the plugin:
- Exposes three OpenClaw tools to your agents:
memory_recall,memory_store,memory_forget. - Adds a CLI sub-command
openclaw ltm {list,search,stats}. - Hooks
before_prompt_buildto auto-recall relevant memories (ifautoRecall: true). - Hooks
agent_endto auto-capture preferences / decisions / entities / facts matching a rule-based trigger list (ifautoCapture: true) — a fast, per-turn safety net, not exhaustive by design. - Registers a
registerMemoryCapabilityflush plan — the same mechanism OpenClaw's own bundledmemory-coreplugin uses. Right before a session auto-compacts, the core runtime prompts the agent to callmemory_storefor anything durable that's about to fall out of context, in its own words. This runs independently ofautoCaptureand catches things the per-turn trigger list misses. No config needed — registered automatically whenever the plugin loads on a host that supports it (silently skipped on older hosts). - Exposes four SynapCores-only methods at
plugin.extensions.*(see "Extensions" below).
API reference
Tools (used by agents at runtime)
| Tool | What it does |
|---|---|
memory_recall | Vector-search the memory store. Params: { query: string, limit?: number } (default 5). |
memory_store | Persist a new memory. Params: { text, importance?, category? }. De-dupes against >0.95 cosine similarity. |
memory_forget | Delete a memory by memoryId (engine-assigned id, e.g. mem_…) or by query (auto-deletes if exactly one candidate at >0.9 similarity, otherwise returns candidates). |
Extensions (programmatic, SynapCores-only)
Reached via plugin.extensions.* after plugin.register(api) runs.
interface MemorySynapCoresExtensions {
/** Vector recall scoped by a SQL WHERE clause. */
recallFiltered(opts: {
where: string; // e.g. "category = 'preference' AND importance >= 0.7"
semantic: string; // natural-language query
limit?: number; // default 5
}): Promise<MemorySearchResult[]>;
/** Walk SIMILAR_TO / MENTIONS / RELATES_TO edges from a memory. */
recallRelated(memoryId: string, opts?: {
hops?: number; // default 1 (capped at 4)
edgeKinds?: string[]; // default: ["SIMILAR_TO"]
similarityThreshold?: number; // default 0.5 (synthetic SIMILAR_TO edges only)
limit?: number; // default 20
}): Promise<RelatedMemoryResult[]>;
/** Score candidates with an AutoML model (with heuristic fallback). */
predictRelevance(query: string, candidates: MemoryEntry[]): Promise<RelevanceScoredMemory[]>;
/** Train (or retrain) the AutoML relevance model from feedback. */
trainRelevanceModel(feedback: Array<{
memoryId: string;
queryText: string;
score: number; // 0..1
}>): Promise<{ modelId: string; modelName: string }>;
}
recallFiltered — SQL-scoped semantic recall
const results = await plugin.extensions.recallFiltered({
where: "category = 'preference' AND importance >= 0.7",
semantic: "what UI style does the user prefer?",
limit: 5,
});
Because the engine cannot apply a WHERE to the table-valued MEMORY_RECALL(?, ?, ?) result-set, the plugin runs an oversampled unfiltered recall and evaluates the where predicate client-side in JS. A malformed clause surfaces as a descriptive recallFiltered: … error thrown by the plugin (not an engine error).
Supported where surface (parsed by the plugin's predicate compiler):
- Fields:
category,importance,createdAt,text/content,id,similarity/score, and JSON-extractmetadata->>'key'for any other metadata field. - Comparison operators:
=/==,!=/<>,>,>=,<,<=,LIKE(SQL%/_wildcards), andIN (…). - Boolean combinators:
AND,OR,NOT, and parentheses. - Literals: single-quoted strings, numbers,
TRUE/FALSE/NULL.
An empty clause or 1=1 passes all rows. Anything outside this surface (subqueries, functions, joins) throws rather than silently returning wrong rows.
recallRelated — graph neighborhood walk
const neighbors = await plugin.extensions.recallRelated(memoryId, {
hops: 1,
edgeKinds: ["SIMILAR_TO"], // default
similarityThreshold: 0.5, // default — cosine threshold for synthetic edges
limit: 20,
});
Returns memories cosine-similar to the source (synthetic SIMILAR_TO edges, single-hop), plus any explicit MENTIONS / RELATES_TO edges the caller has populated (multi-hop supported on non-synthetic edge kinds). Requires autoLinkSimilar: true at capture time — the plugin inserts each Memory as a graph node carrying the embedding so the gateway's vector-indexed synthetic edges resolve at MATCH time.
If a source memory was captured before autoLinkSimilar was enabled, its Memory graph node won't exist and recallRelated will return [] for it. Re-capture (or write a one-off back-fill that posts {labels: ["Memory"], properties: {id, text, embedding, ...}} to /v1/graph/nodes) to retro-fit.
predictRelevance — AutoML re-ranking with heuristic fallback
const top = await plugin.extensions.recallFiltered({ where: "1=1", semantic: query, limit: 20 });
const ranked = await plugin.extensions.predictRelevance(query, top.map((r) => r.entry));
ranked.sort((a, b) => b.relevance - a.relevance);
When a model named openclaw_memory_relevance[_<workspace>] exists, candidates are scored by it. Otherwise the plugin falls back to:
relevance = 0.6 * (cosine_similarity(query, memory) + 1) / 2 # cosine mapped [-1,1] -> [0,1]
+ 0.25 * exp(-age_days / 14) # ~14-day recency decay
+ 0.15 * memory.importance
trainRelevanceModel — promote feedback to a model
const feedback = [
{ memoryId: "...", queryText: "what's my email?", score: 1.0 },
{ memoryId: "...", queryText: "dark mode preference", score: 0.9 },
// ... at least 10 samples
];
await plugin.extensions.trainRelevanceModel(feedback);
// `predictRelevance` will automatically pick up the new model on the next call.
Requires at least 10 samples; throws otherwise. Train periodically (cron / on-demand) — the next predictRelevance call will detect the model and switch out of heuristic mode.
Under the hood, the plugin stages feedback rows in a SQL table (openclaw_memory_relevance_training[_<workspace>]) on the gateway, then calls /v1/automl/train with target: 'score' and task: 'regression'. The table is preserved across calls so feedback accumulates between sessions; clear it manually with DROP TABLE (via client.executeQuery) if you want a clean restart. Memory hydration is via MEMORY_RECALL(?, ?, ?) WHERE id = ? against the engine's namespace; rows whose memories have been deleted are skipped.
Roadmap
- Entity extraction on capture — parse
@mentiontokens and known-contact names out of incoming text and createPerson/Projectgraph nodes withMENTIONSedges back to the memory. - Tag inference — auto-classify memories into a configurable tag vocabulary on capture (small classifier or LLM call) so
recallFilteredqueries can use tags out of the box. synapcores-import-lancedbmigration script — read an existing~/.openclaw/memory/lancedbstore, re-embed if needed, and bulk-load into a SynapCores collection. Ships as abinentry on the package.- Drop the
_getHttpClientgraph-node / graph-match workarounds once the SDK restores a native graph API:client.graph.nodes.createneeds to post{labels: [label]}(not{label}) to match the gateway's/v1/graph/nodeshandler, andrecallRelatedcurrently posts Cypher to/v1/graph/matchdirectly because@synapcores/sdk@^0.6.0no longer exposesclient.graph.cypher.
Upstream
OpenClaw PR adding this plugin to the upstream extension catalogue: TBD — link will be added once the PR opens.
License
MIT. See LICENSE.
