openclaw-hybrid-memory
v2026.8.4
Published
Give your OpenClaw agent lasting memory: structured facts, semantic search, auto-capture & recall, decay, optional credential vault. Part of Hybrid Memory v3.
Maintainers
Readme
OpenClaw memory-hybrid plugin (npm: openclaw-hybrid-memory)
This folder is the published OpenClaw extension: durable agent memory (structured store + semantic recall, auto-capture / auto-recall, configurable decay and maintenance, optional graph and credential vault).
User-facing overview, scenarios, and install: Repository README · Documentation site · Quick start
Install & verify
openclaw plugins install openclaw-hybrid-memory
openclaw hybrid-mem install
# Configure embedding (required) in ~/.openclaw/openclaw.json — see LLM-AND-PROVIDERS.md
openclaw gateway stop && openclaw gateway start
openclaw hybrid-mem verifyUpgrade: openclaw hybrid-mem upgrade (then restart the gateway).
Useful follow-up commands: openclaw hybrid-mem status for a one-screen health summary and openclaw hybrid-mem dashboard for the Mission Control URL.
Requirements (short)
| Requirement | Notes |
|-------------|--------|
| Node.js | >=22.16.0 (engines in package.json) |
| OpenClaw | ≥2026.5.0 required (peer); ≥2026.6.1 recommended (Skill Workshop, skills hot-reload, Dreaming tab) |
| Embeddings | Required — OpenAI, Ollama, ONNX, or Google; see LLM-AND-PROVIDERS.md |
| Build toolchain | For @lancedb/lancedb: C++ build tools + Python 3 on the install machine |
Agent tools
All tools use underscore names (memory_store, memory_recall, …). Dotted aliases are invalid for some providers.
Package layout
| Path | Role |
|------|------|
| openclaw.plugin.json | Manifest and config schema |
| index.ts | Plugin entry: stores, tools, CLI, lifecycle |
| config.ts | Defaults and config parsing |
| backends/ | SQLite, LanceDB, event bus, etc. |
| tools/ | Tool implementations and dashboard routes |
| cli/ | hybrid-mem commands |
| skills/hybrid-memory/ | Bundled Agent Skill (SKILL.md + references); copied to {workspace}/skills/hybrid-memory/ on first plugin start if absent (or use hybrid-mem install to refresh) |
| File | Description |
|------|-------------|
| package.json | npm package and OpenClaw extension entry |
| openclaw.plugin.json | Plugin manifest and config schema |
| config.ts | Decay classes, TTL defaults, config parsing (incl. autoRecall, store, etc.) |
| index.ts | Plugin implementation (SQLite+FTS5, LanceDB, tools, CLI, lifecycle) |
| versionInfo.ts | Plugin and memory-manager version metadata |
| backends/event-bus.ts | Event Bus — append-only memory_events SQLite table for sensor → maintenance/Dream pipeline |
| tools/dashboard-routes.ts | Dashboard HTTP route registration — registers all /plugins/memory-dashboard/* routes with consistent auth (Issue #279) |
| tools/public-api-routes.ts | Public API surface routes (/plugins/memory-public/*) for health/search/timeline/stats/export/fact (Issue #1027) |
| services/public-export-bundle.ts | Stable export bundle builder used by GET /plugins/memory-public/export (Issue #1027) |
| Topic | Doc | |--------|-----| | Full config | CONFIGURATION.md | | CLI | CLI-REFERENCE.md | | Architecture | ARCHITECTURE.md | | Event bus API | docs/event-bus.md | | Retrieval / RRF | docs/rrf-retrieval.md, RETRIEVAL-MODES.md | | Graph / contacts | GRAPH-MEMORY.md | | ONNX embeddings | README below |
Entity layer (contacts, organizations, NER)
When graph.enabled is true, new facts are enriched asynchronously with typed entity mentions (e.g. PERSON, ORG, SERVICE, TOOL, MODEL, PROJECT, AGENT, ROLE) using franc + LLM with quality gating/canonicalization. Before mention rows are written to SQLite (organizations, contacts, fact_entity_mentions, org_fact_links), they are normalized, deduplicated per fact/label, and filtered for short or generic noise. The memory_directory tool exposes list_contacts and org_view for structured lists—use memory_recall for ranked semantic search. Backfill older facts with openclaw hybrid-mem enrich-entities, and audit/repair existing rows with openclaw hybrid-mem entity-mentions audit|cleanup. See GRAPH-MEMORY.md and MULTILINGUAL-SUPPORT.md.
Event Bus
backends/event-bus.ts adds an Event Bus: an append-only memory_events SQLite table that decouples sensor sweeps (producers) from maintenance and Dream consumers (see docs/event-bus.md; no separate Rumination Engine module).
Key API:
| Method | Description |
|--------|-------------|
| appendEvent(type, source, payload, importance?, fingerprint?) | Append a new event; returns its auto-generated id |
| queryEvents(filter?) | Filter by status, event_type, since, limit |
| updateStatus(id, newStatus) | Advance an event through the status lifecycle |
| dedup(fingerprint, cooldownHours?) | Return true if a duplicate exists within the cooldown window |
| pruneArchived(olderThanDays?) | Delete archived events older than N days |
Status lifecycle: raw → processed → surfaced → pushed → archived
computeFingerprint(input) is a SHA-256 helper for building stable dedup keys.
See docs/event-bus.md for the full schema, API reference, and integration example.
Dashboard HTTP Routes
tools/dashboard-routes.ts registers two HTTP routes under the /plugins/memory-dashboard/ prefix:
| Route | Description |
|-------|-------------|
| GET /plugins/memory-dashboard/ | HTML dashboard shell |
| GET /plugins/memory-dashboard/api/health | JSON health report ({ status, generatedAt }) |
Routes are only registered when health.enabled is true (the default). OpenClaw v2026.3.8 enforces a consistent-auth requirement: every route under the same path prefix must use the same authenticated value. dashboard-routes.ts satisfies this by reading cfg.health.authenticated once and applying it to all routes via a single shared routeOpts object.
Config field: health.authenticated (boolean, default true) — controls whether dashboard routes require an authenticated session. Set to false only if you intentionally want unauthenticated access.
Mission Control — Memory Viewer (Issue #1023)
The Mission Control local dashboard (createDashboardServer) doubles as the Memory Viewer / Mission Control UI for hybrid-memory. It serves a rich HTML dashboard at the root and exposes a comprehensive JSON API under /api/viewer/.
Access: The dashboard runs on 127.0.0.1 only (local-only, no authentication required for local access). The HTTP server port is configured via dashboard.port in the plugin config (default 7700).
Important: The dashboard server is registered separately from the OpenClaw HTTP gateway. It is accessible at
http://127.0.0.1:7700/(or the configured port) — not through the OpenClaw gateway URL. This is intentional: local-only, no auth overhead, safe for operators on the same machine.
Memory Viewer API endpoints
All Memory Viewer routes are served by the local Mission Control server on the dashboard port:
| Endpoint | Method | Description |
|----------|--------|-------------|
| /api/viewer/stats | GET | Overview: total facts, verified, edicts, issues, episodes, links, breakdown by category/tier/decay/source |
| /api/viewer/facts | GET | Paginated facts list with optional filters: ?category=&entity=&limit=&offset= |
| /api/viewer/facts/:id | GET | Single fact detail including verification status, provenance, and decay info |
| /api/viewer/facts/:id/verify | POST | Verify a fact ({ "verifiedBy": "agent" \| "user" \| "system" }) |
| /api/viewer/facts/:id/forget | POST | Forget (soft-delete) a fact |
| /api/viewer/entities | GET | Top entities by fact count with categories and tags |
| /api/viewer/episodes | GET | Recent episodic memory events with outcome, duration, and session context |
| /api/viewer/narratives | GET | Recent session narratives/summaries |
| /api/viewer/issues | GET | All tracked issues with status, severity, and symptoms |
| /api/viewer/workflows | GET | Workflow patterns and recent tool-sequence traces |
| /api/viewer/edicts | GET | All edicts (verified ground-truth facts) |
| /api/viewer/verified | GET | All verified facts with verification metadata |
| /api/viewer/links | GET | Memory graph links (source → target with type and strength) |
| /api/viewer/provenance/:factId | GET | Provenance chain for a specific fact |
All responses include Cache-Control: no-cache. POST endpoints accept JSON body and return { ok: boolean, message: string }.
Public API HTTP Routes
tools/public-api-routes.ts registers a compact, beginner-friendly REST surface under /plugins/memory-public/:
| Route | Description |
|-------|-------------|
| GET /plugins/memory-public/health | API surface health + version metadata |
| GET /plugins/memory-public/search?q=<query>&limit=<n> | Simple full-text memory search |
| GET /plugins/memory-public/timeline?limit=<n> | Reverse-chronological memory timeline |
| GET /plugins/memory-public/stats | Core memory stats (facts/episodes/procedures/links) |
| GET /plugins/memory-public/export | Stable JSON export bundle (facts, episodes, procedures, narratives, provenance) |
| GET /plugins/memory-public/fact?id=<uuid> | Inspect a single fact plus incoming/outgoing links |
Routes use the same health.authenticated setting to keep auth behavior consistent per prefix.
See PUBLIC-API-SURFACE.md for demo flows and payload shape details.
"health": {
"enabled": true,
"authenticated": true
}Lifecycle & Shutdown
The plugin registers SIGUSR1 / SIGUSR2 signal handlers to close all database connections cleanly on process shutdown:
import { closeAllDatabases } from "./backends/base-sqlite-store";
// Called automatically on SIGUSR1/SIGUSR2, or invoke manually:
await closeAllDatabases();All stores (FactsDB, CredentialsDB, EventBus, etc.) implement a close() method that:
- Flushes pending writes
- Closes the SQLite connection
- Prevents further operations (throws
"<StoreName> is closed"if accessed afterward)
The EventBus additionally enters a terminal closed state after close() is called — any subsequent appendEvent() call throws "EventBus is closed".
Dependencies
- Built-in
node:sqlite(ships with supported Node.js versions) @lancedb/lancedb^0.31.0@sinclair/typebox^0.34.52openai^6.46.0 — peer dependency (must be directly provided by the host). Theopenaipackage is not bundled with this plugin. Your host environment must directly declare and installopenai ^6.46.0— a transitive copy (e.g. one pulled in via a sub-dependency of OpenClaw) is not sufficient under pnpm, Yarn PnP, or other strict package managers. Install it explicitly alongside this plugin:npm i openai.
Build tools required for @lancedb/lancedb: C++ toolchain (e.g. build-essential on Linux, Visual Studio Build Tools on Windows), Python 3.
Local ONNX Embeddings (optional)
For local embedding inference without an API key, install onnxruntime-node into the OpenClaw extensions folder (~/.openclaw/extensions) — one level above the plugin package — so that it survives openclaw hybrid-mem upgrade:
npm install --prefix ~/.openclaw/extensions onnxruntime-node@^1.20.0Then set embedding.provider: "onnx" in plugin config. See repository TROUBLESHOOTING.md if loading fails.
Recall timing diagnostics
Optional autoRecall.recallTiming (off | basic | verbose) — see INTERACTIVE-RECALL-LATENCY.md and CONFIGURATION.md.
Credits
Design lineage and a full list of extensions in this repo: CREDITS-AND-ATTRIBUTION.md. Based on Give Your Clawdbot Permanent Memory (Clawdboss.ai).
Operator health audit
Run a one-shot store health report with:
openclaw hybrid-mem audit health
openclaw hybrid-mem audit health --json
openclaw hybrid-mem audit health --strictThe JSON output is versioned (schemaVersion: 1) for dashboards and automation. The report surfaces tier sanity, category drift, vectorless active facts, validated-but-unpromoted procedures, implicit-feedback signal noise, and remediation hints. --strict exits 2 when warnings/errors are present; JSON includes exitCode, exitReason, warningCount, and errorCount so cron/automation can treat strict health failures differently from command crashes. The installer also publishes a weekly hybrid-mem:weekly-audit-health cron step that runs openclaw hybrid-mem audit health --strict --json.
For LanceDB/Arrow/native RSS diagnostics, run:
openclaw hybrid-mem vectordb-health
openclaw hybrid-mem vectordb-health --jsonHybrid-memory emits one-time startup-memory-checkpoint log lines for plugin registration, first recall, first active-task projection, and first compaction (recorded on the first before_compaction hook). Each line includes owner, subsystem, operation, RSS bytes/delta, and active handle/request counts for faster attribution during gateway memory pressure incidents.
Vector search and semantic-cache result materialization are bounded at runtime to prevent unbounded Arrow/Lance memory use under heavy recall workloads. The defaults can be tuned via environment variables:
| Variable | Default | Min | Max | Description |
|---|---|---|---|---|
| OPENCLAW_HYBRID_MEM_VECTOR_QUERY_MAX_RESULTS | 200 | 10 | 5000 | Hard ceiling for rows fetched per vector search call |
| OPENCLAW_HYBRID_MEM_SEMANTIC_CACHE_MAX_ROWS_PER_FILTER_KEY | 100 | 10 | 5000 | Per-filter cap for semantic query cache rows |
| OPENCLAW_HYBRID_MEM_SEMANTIC_CACHE_CANDIDATE_LIMIT_MAX | 200 | 10 | 5000 | Upper bound for candidate vectors loaded per cache lookup |
hybrid-mem stats and hybrid-mem health now also surface decay stickiness (stable+permanent) and guidance to run decay reclassify --dry-run --stable-only when legacy ratios are high.
Weekly pending digest (#1197)
Render an at-a-glance backlog of approve/decline/defer actions across persona proposals, procedure promotions, tool proposals, crystallization proposals, and verified facts:
openclaw hybrid-mem digest pending --since 7d --format md
openclaw hybrid-mem digest pending --since 7d --format json --out /tmp/pending.jsonThe JSON payload is versioned (schemaVersion: 1) and surfaces:
personaProposals.pendingEntries[].evidence— top supporting fact ids and total count derived from the proposal'sevidenceSessionsjoin againstfacts.provenance_session.procedures.newThisWeek— count of procedures whoselast_validatedtimestamp falls within the lookback window, alongside the existingvalidatedNotPromotedbacklog.- Approve / decline / defer commands per entry so the operator can act without leaving the digest.
The installer registers hybrid-mem:weekly-pending-digest (Mondays 08:00) and respects
digest.weekly.delivery.{ mode, chatId } to optionally announce the rendered markdown body to a
Telegram channel. Full schema and rationale: docs/pending-digest.md.
Maintenance log format & analyzer
Hybrid-memory maintenance cron jobs write structured run artifacts under:
$HOME/.openclaw/logs/cron-hybrid-mem/YYYYMMDD/
<job>-<timestamp>-<pid>.log
<job>-<timestamp>-<pid>.exit.txtOrchestrator runs may also write {job}-{runId}.summary.json and {job}-{runId}.validation.json (see docs/maintenance-job-runs.md). Per-command JobRun artifacts live under job-runs/{jobRunId}/.
Each .exit.txt file contains one line per step:
2026-05-07T02:10:21Z prune exit=0
2026-05-07T02:11:02Z distill exit=1
2026-05-07T02:12:10Z self-correct exit=0 status=skipped reason=skipped_cooldownUse the analyzer to classify failures, persist regression history, emit an operator digest, and optionally report plugin/orchestration bugs through the existing GlitchTip path:
openclaw hybrid-mem maintenance analyze-logs --since 24h --digest md
openclaw hybrid-mem maintenance analyze-logs --since 7d --format json --out /tmp/maintenance-findings.json
openclaw hybrid-mem maintenance analyze-logs --since 24h --auto-fix --glitchtip --strictRules are data-driven in services/maintenance-rules.json, so operators can inspect or extend classifications without changing analyzer code. Findings are persisted in maintenance-findings.db table maintenance_finding, enabling week-over-week trend output for --since 7d / --trend runs. The digest collapses repeated fingerprints into New vs Still failing, suppresses stale historical fingerprints outside the current window from the primary findings, and avoids re-reporting already GlitchTip-reported fingerprints on every analyzer run. Known benign compatibility notices (for example missing registerContextEngine on older SDKs and Codex project-local config warnings) are collected in noiseWarnings[] and excluded from primary failure counts by default (#1833).
The installer registers hybrid-mem:maintenance-log-analyzer to run after the nightly chain and announce the rendered digest to the operator.
Unified maintenance inventory
Use the maintenance inventory report to see host crontab jobs and gateway ~/.openclaw/cron/jobs.json jobs in one place, including scheduler ownership, timezone, guard/log paths, last-run state, and collision groups:
openclaw hybrid-mem maintenance inventory
openclaw hybrid-mem maintenance inventory --format markdown
openclaw hybrid-mem maintenance inventory --jsonAuto-fix whitelist (#1199)
--auto-fix applies only safe, idempotent actions implemented in services/maintenance-auto-fix.ts:
- Stale scan lock files: when a finding includes a
*.lockabsolute path and the recorded PID is not running, the lock file is removed. - Retry-once marker: for transient LLM / network rules, the finding is annotated (
auto-fixed-retry-once) and a persisted per-step marker is written under~/.openclaw/cron/guard/step--<name>.retry-once(#2094). The orchestrator's guard check consumes this marker on the step's next evaluation, forcing exactly one run outside its normal cadence window even if the guard interval hasn't elapsed yet — no extra shell commands are executed.
Add --auto-fix-all together with --auto-fix to opt into heavier remediation (still scoped to the maintenance analyzer):
- Vacuum-on-busy: when at least two
SQLITE_BUSY/ “database is locked” findings fall within the configured persistence window (historical rows inmaintenance-findings.dbplus the current batch), runsVACUUM+ WAL checkpoint on the open facts DB and invokesopenclaw hybrid-mem vectordb-optimize. - Re-embed vectorless: when an
embedding-authrule matches in the current batch, runsopenclaw hybrid-mem reembed-vectorless --limit 200 --applyonce (useful after credentials were fixed).
Override the CLI binary with OPENCLAW_BIN if openclaw is not on PATH.
Resolved-issue suppression
services/maintenance-resolved.json maps finding fingerprints to { resolvedInVersion, note }. When the log’s parsed pluginVersion is >= resolvedInVersion (dotted numeric comparison), matching findings are dropped from the analyzer output to cut noise after a release fix.
