@synapcores/openclaw-memory
v0.4.0
Published
OpenClaw long-term memory plugin backed by SynapCores AIDB engine-side MEMORY_STORE/RECALL/FORGET primitives + SQL filtering, graph relations, AutoML relevance
Maintainers
Readme
@synapcores/openclaw-memory
A long-term memory plugin for OpenClaw that uses SynapCores AIDB as the storage backend. Drop-in alternative to @openclaw/memory-lancedb with the same auto-recall / auto-capture lifecycle, plus four SynapCores-only extensions: SQL-filtered semantic recall, graph-relation walks, AutoML relevance scoring, and a model-training helper.
0.4.0 shipping note: the core memory ops (
memory_store/memory_recall/memory_forget) now ride the engine-sideMEMORY_STORE/MEMORY_RECALL/MEMORY_FORGETprimitives via@synapcores/sdk@^0.5.0'sclient.memorysurface. The plugin's public API (tools, CLI, extensions, types) is unchanged — only the internal storage path moved.
- Requires SynapCores gateway
v1.8.5-ceor newer (the version that ships theMEMORY_*SQL functions).- Embedding moved server-side for the hot path.
memory_store/memory_recallno longer call OpenAI — the gateway embeds via its configured model. The OpenAI client is still used by the relevance extensions (predictRelevance,trainRelevanceModel) and theautoLinkSimilargraph-node embedding.- 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 run engine-side against theMEMORY_RECALL(?, ?, ?)result-set. The plugin auto-rewrites the four legacy column shorthands (category,importance,createdAt,text) into the JSON-extract form so most existing call sites keep working unchanged.
Upgrading from 0.3.x
@synapcores/[email protected] is a hard cut: the storage backend changes, so old memories will not migrate automatically. Steps:
- Upgrade the SynapCores gateway to
v1.8.5-ceor newer. npm install @synapcores/[email protected].- 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 via auto-rewrite. Callers using the more general SQL surface should switch to the JSON-extract form (metadata->>'…') directly.
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-memoryopenclaw (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 startCreate 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-memoryAdd 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,
"config": {
"embedding": {
"apiKey": "${OPENAI_API_KEY}",
"model": "text-embedding-3-small"
},
"synapcores": {
"host": "localhost",
"port": 8080,
"apiKey": "${SYNAPCORES_API_KEY}",
"useHttps": false
},
"collection": "openclaw_memories",
"graph": "openclaw_memory_graph",
"autoCapture": true,
"autoRecall": true,
"autoLinkSimilar": true
}
}
}
}
}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.
Then openclaw config validate. Environment-variable interpolation
(${OPENAI_API_KEY}, ${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.)
Config fields
| Field | Required | Default | Notes |
| --- | --- | --- | --- |
| embedding.apiKey | yes | — | OpenAI API key for the embedding model. |
| embedding.model | no | text-embedding-3-small | Either text-embedding-3-small (1536 dims) or text-embedding-3-large (3072 dims). |
| 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-80ms 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_agent_startto auto-recall relevant memories (ifautoRecall: true). - Hooks
agent_endto auto-capture preferences / decisions / entities / facts (ifautoCapture: true). - 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 (UUID) 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
edgeKinds?: string[]; // default: any
}): 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,
});The where clause is forwarded to the SynapCores gateway as the filter field on /vector_search. SQL validation happens on the gateway — a malformed clause will surface as an SDK error.
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)
+ 0.25 * exp(-age_days / 14) # 14-day half-life
+ 0.15 * memory.importancetrainRelevanceModel — 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, v0.4.0 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 (0.5.0+)
- 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 workaround once@synapcores/sdk >0.4.0fixesclient.graph.nodes.createto post{labels: [label]}instead of{label}(the wire shape the gateway's/v1/graph/nodeshandler expects).
Upstream
OpenClaw PR adding this plugin to the upstream extension catalogue: TBD — link will be added once the PR opens.
License
MIT. See LICENSE.
