# Nexus Memory — AGENTS.md (AI Installation Guide) > Tell your AI agent: "Read https://raw.githubusercontent.com/Neboy72/nexus-memory/main/AGENTS.md and follow the installation instructions." ## Overview Nexus Memory is a **universal memory layer** for AI agents. One memory for all your agents — Hermes, OpenClaw, Claude Code, Cursor, or any MCP-compatible agent. ## Hermes Native Plugin (Recommended for Hermes Agent) If you use **Hermes Agent**, install Nexus Memory as a native MemoryProvider plugin. This gives you direct Qdrant access with zero MCP overhead — Hermes reads/writes memories as part of its core loop. ### One-Command Install ```bash cd ~/nexus-memory ./scripts/install_hermes_plugin.sh ``` This script: 1. Symlinks the `nexus` plugin into `~/.hermes/hermes-agent/plugins/memory/` 2. Sets `memory.provider` to `nexus` in Hermes config 3. Verifies the installation ### Manual Setup Or, run these commands yourself: ```bash # Link the plugin ln -s ~/nexus-memory/plugins/memory/nexus ~/.hermes/hermes-agent/plugins/memory/nexus # Activate it hermes config set memory.provider nexus ``` ### Verify ```bash hermes config get memory.provider # → nexus ``` Restart Hermes Gateway. Nexus tools appear as `nexus_recall`, `nexus_remember`, `nexus_forget`. ## OpenClaw Native Plugin (Recommended for OpenClaw) If you use **OpenClaw**, install Nexus Memory as a native memory plugin. This gives you Auto-Recall (memories injected before every turn) and Auto-Capture (facts extracted after every turn) — all powered by your local Qdrant. ### One-Command Install ```bash cd ~/nexus-memory ./scripts/install_openclaw_plugin.sh ``` This script: 1. Detects your OpenClaw installation 2. Adds the plugin to `plugins.load.paths` in `~/.openclaw/openclaw.json` 3. Sets `plugins.slots.memory = "nexus-memory"` 4. Auto-detects your embedding provider (Voyage > OpenAI > Google > Jina > Ollama) 5. Restarts OpenClaw gateway ### Manual Setup Or, run these commands yourself: 1. Add plugin path to `~/.openclaw/openclaw.json`: ```json { "plugins": { "load": { "paths": ["/path/to/nexus-memory/plugins/openclaw"] }, "slots": { "memory": "nexus-memory" }, "entries": { "nexus-memory": { "enabled": true, "hooks": { "allowPromptInjection": true, "allowConversationAccess": true }, "config": { "qdrantUrl": "http://localhost:6333", "collection": "nexus", "embedding": { "provider": "voyage", "model": "voyage-4", "apiKey": "${VOYAGE_API_KEY}" }, "autoRecall": true, "autoCapture": true, "maxRecallResults": 10, "accessLevel": "private" } } } } } ``` 2. Restart OpenClaw: ```bash openclaw gateway restart ``` ### Verify ```bash openclaw plugins list # → nexus-memory should appear openclaw gateway status # → should show nexus-memory in loaded plugins ``` Restart OpenClaw Gateway. Nexus tools appear as `nexus_search`, `nexus_store`, `nexus_forget`. ### How it works - **Auto-Recall**: Before every agent turn, the plugin searches Qdrant for relevant memories and injects them as a `` block in the system prompt. - **Auto-Capture**: After every agent turn, the plugin extracts the conversation and stores it in Qdrant. - **Tools**: `nexus_search`, `nexus_store`, `nexus_forget` available to the agent. - **Shared store**: Same Qdrant collection as Hermes plugin and MCP server — one brain, many agents. ## Shared Store: One Qdrant, Two Access Paths Nexus Memory uses a single Qdrant collection (`nexus`) backed by one embedder. The Hermes native plugin, the OpenClaw native plugin, and the MCP server all read/write the **same store** — same vectors, same metadata, same access levels. ``` ┌──────────────────────┐ ┌──────────────────────┐ ┌──────────────────────┐ │ Hermes Agent │ │ OpenClaw │ │ Claude Code / Cursor│ │ (Native Plugin) │ │ (Native Plugin) │ │ Codex / Any MCP │ │ │ │ │ │ (MCP Client) │ └──────────┬───────────┘ └──────────┬───────────┘ └──────────┬───────────┘ │ qdrant_client │ TS+fetch() │ stdio │ (direct) │ (Qdrant REST) │ ▼ ▼ ▼ ┌──────────────────────────────────────────────────────────────────┐ │ Qdrant (localhost:6333) │ │ Collection: "nexus" │ └──────────────────────────────────────────────────────────────────┘ ``` > **Key insight:** A memory stored by Hermes via the native plugin is immediately visible to OpenClaw via its plugin and to Claude Code via MCP — and vice versa. One brain, many agents. ### Which path should I use? | Path | Best for | Setup | Memory mode | |------|----------|-------|-------------| | **Native Plugin** | Hermes Agent, OpenClaw | `./scripts/install_hermes_plugin.sh` or `./scripts/install_openclaw_plugin.sh` | **Automatic** — Auto-Recall + Auto-Capture | | **MCP Server** | Claude Code, Cursor, Codex, any MCP agent | `nexus-memory` (stdio) | **Manual** — explicit tool calls | ## Quick Install (MCP Server) ### Prerequisites - Python 3.11+ - Qdrant running on localhost:6333 - At least **one** embedding provider (auto-detected in this order): | Provider | Type | Dimensions | How to get | |----------|------|-----------|------------| | **Voyage** ☁️ | Cloud | 1024d | `VOYAGE_API_KEY` | | **OpenAI** ☁️ | Cloud | 1536d | `OPENAI_API_KEY` | | **Google/Vertex AI** 💚 | Cloud | 768d | `GOOGLE_API_KEY` | | **Jina** 💜 | Cloud | 1024d | `JINA_API_KEY` | | **Ollama** 🦙 | Local | 1024d (bge-m3) | Auto-detected — `ollama pull bge-m3` (recommended, 1.2 GB) or any `embed` model | | **sentence-transformers** 🏠 | Local | 384d | `pip install sentence-transformers` | > **Zero-setup:** If you have Ollama running with an embedding model, it works out of the box — no API key needed. Recommended local model: `ollama pull bge-m3` (1.2 GB, 1024d, multilingual, best local quality). Smaller alternative: `nomic-embed-text` (274 MB, English-focused). ### 1. Install ```bash git clone https://github.com/Neboy72/nexus-memory.git ~/nexus-memory cd ~/nexus-memory pip install -e . ``` ### 2. Configure Set your preferred embedding provider's API key. Pick **one** of these options — the server auto-detects which provider is available: **Option A — MCP `env:` block (recommended):** ```json // OpenClaw — mcp.servers..env // Hermes / Claude Code / Standard MCP — mcpServers..env { "env": { "VOYAGE_API_KEY": "vo-your-key-here" // or: "OPENAI_API_KEY": "sk-...", // or: "GOOGLE_API_KEY": "AIza..." } } ``` **Option B — `.env` file (auto-loaded from repo root or $NEXUS_ENV_FILE):** ```bash echo 'VOYAGE_API_KEY="vo-your-key-here"' >> ~/nexus-memory/.env ``` > 💡 **No API key?** If you have Ollama running locally with an embedding model (e.g. `bge-m3`), skip config entirely — the server detects it automatically. ### 3. Run MCP Server ```bash nexus-memory ``` The server starts on stdio and auto-creates a `nexus` collection in Qdrant. ### 4. Connect your Agent **Hermes Agent** — add to `~/.hermes/config.yaml`: ```yaml mcp_servers: nexus: command: /path/to/venv/bin/python3 args: ["-m", "nexus_memory.mcp_server"] env: PYTHONPATH: /Users/you/nexus-memory ``` Restart gateway. Tools appear as `mcp_nexus_remember`, `mcp_nexus_recall`, etc. **Any MCP-compatible agent** — configure to launch: ```json { "mcpServers": { "nexus": { "command": "nexus-memory" } } } ``` ### 5. 🌐 Web UI (optional) ```bash pip install nexus-memory[webui] nexus-memory webui ``` Opens a live graph dashboard at `http://127.0.0.1:9120` — explore your memory network visually. ## Available Tools (15) > **3 integration paths, all with guardrails:** Native Plugin (Hermes, OpenClaw, Claude Code) | MCP Server (any MCP agent) | Tool | Description | Parameters | |------|-------------|------------| | `remember` | Store a memory | `text` (req), `category` (req, default `fact`), `access_level`, `source`, `source_url`, `confidence` | | `recall` | Hybrid search — returns results with `verification` status | `query` (req), `limit`, `filter_level`, `as_of` (point-in-time: `YYYY-MM-DD`, optional) | | `forget` | Delete a memory | `memory_id` (req) | | `update` | Update in-place, preserve metadata | `memory_id` (req), `text`, `modified_by` | | `health` | Check server status | — | | `check_update` | Check for newer version on GitHub | — | | `do_update` | Update + restart server | `confirm` (req, must be `true`) | | `subscribe` | Register a webhook for a memory event | `event_type` (req), `webhook_url` (req) | | `unsubscribe` | Remove a webhook subscription | `subscription_id` (req) | | `list_subscriptions` | List all registered webhook subscriptions | — | | `backup` | Manual backup of all memories to JSON | — | | `restore` | Restore memories from backup JSON | `backup_path` (req), `reembed` (optional) | | `guardrail_check` | Check if an action is safe before executing (queries protection rules in memory) | `command` (req), `tool_name`, `tool_input` | | `guardrail_override` | Record a guardrail override with audit trail (requires explicit reasoning) | `command` (req), `reasoning` (req, min 10 chars), `matched_rules`, `agent_id` | | `cost_routing_stats` | Get statistics about embedding provider routing | — | | `cost_routing_explain` | Explain the routing decision for a memory category | `category` (req) | | `nexus_sica_run` | Run a SICA self-improvement cycle (scan for stale temp, low confidence, contradictions) | `auto_patch` (optional, default true) | ## Graph-Boosted Auto-Recall **New in v0.9.0.** Auto-Recall now doesn't just search by vector similarity — it also fetches 1-hop graph neighbors from the top 3 vector search results via `GraphTraversal.get_related()`. ### How it works 1. Vector search runs normally (top-N results) 2. For the top 3 results, the plugin fetches their graph edges (1-hop neighbors) 3. Neighboring points are fetched and added to the context output with `[graph:]` tags 4. Graph items are capped at 5 to prevent context bloat 5. Access-level filtering applies to graph neighbors (OpenClaw + Claude Code plugins) ### Available in - **Hermes plugin** (Python) — `_graph_boost()` in prefetch + recall - **OpenClaw plugin** (TypeScript) — `graphBoost()` in before_prompt_build hook - **Claude Code plugin** (Python) — `graph_boost()` in auto_recall hook ### Example When you search for "Wallbox", you get: - Vector hits about the ABL Wallbox (similarity match) - Graph neighbors: Reev Backend (`[graph:connected_to]`), RFID cards (`[graph:uses]`), IP address (`[graph:located_at]`) This gives the agent **contextual relationships**, not just text similarity. ## SICA Self-Improvement Cycle **New in v0.9.0.** SICA (Self-Improving Cycle for Agents) automatically scans memories for issues and patches them. ### How it works ``` Detect → Reflect → Act → Learn ``` 1. **Detect**: Scans all memories for: - Stale temp memories (older than configurable threshold, default 7 days) - Low-confidence memories (below 0.5 confidence) - Contradictions (conflicting beliefs via graph edges) 2. **Reflect**: Categorizes issues and generates suggestions 3. **Act**: Auto-patches non-destructive issues (stale temp deletion). Other issues become suggestions for review. 4. **Learn**: Stores the SICA session as a memory for future iterations ### Usage ```bash # Via Hermes plugin tool nexus_sica_run(auto_patch=True) # Via Python (harness-independent) from nexus.sica import run_sica result = run_sica(auto_patch=True) ``` ### Configuration | Env Var | Default | Description | |---------|---------|-------------| | `SICA_STALE_TEMP_DAYS` | 7 | Days before temp memories are considered stale (legacy; feeds SICA_RETENTION_TEMP as fallback) | | `SICA_RETENTION_TEMP` / `SICA_RETENTION_SESSION` / `SICA_RETENTION_` | 1 / 7 / — | Per-category retention in days (roadmap 2.2). Categories without a policy keep memories forever | | `SICA_DEFAULT_RETENTION_DAYS` | — | Fallback retention for categories without explicit policy | | `NEXUS_RERANK` / `NEXUS_RERANKER` / `NEXUS_RERANK_POOL` | off / auto / 20 | Cross-encoder reranking (roadmap 1.2): auto = Voyage when key present, free local cross-encoder otherwise | | `NEXUS_PREFETCH_CHARS` | 1200 | Token budget for auto-injected memory context (roadmap 3.1b) | | `NEXUS_AUTO_ENRICH` | 1 | Auto entity enrichment on nexus_remember (0 = off) | | `SICA_RETENTION_*` etc. also accept `SICA_STALE_TEMP_DAYS` | — | Legacy single-category knob still works | | `SICA_LOW_CONFIDENCE` | 0.5 | Confidence threshold for low-confidence detection | | `SICA_MAX_SUGGESTIONS` | 10 | Maximum suggestions per SICA run | ## Active Guardrails **New in v0.5.0.** Nexus Memory doesn't just store and retrieve — it actively prevents destructive actions. Before any destructive operation (`rm -rf`, `drop`, `kill -9`, `recreate_collection`), the guardrail checks Qdrant for stored protection rules (category=`rule` with protection keywords like "never delete", "protected", "do not remove"). If the target matches a protected path or collection, the action is **blocked**. ### How it works 1. Store a protection rule: `remember(text="Never delete ~/nexus-memory-test/ - sandbox test environment", category="rule")` 2. Before a destructive command, call: `guardrail_check(command="rm -rf ~/nexus-memory-test/")` 3. If blocked, the agent must call `guardrail_override(command="...", reasoning="User explicitly authorized cleanup after backup")` to proceed with an audit trail ### Pattern detection `rm -rf`, `rmdir`, `del /f`, `drop`, `truncate`, `kill -9`, `pkill`, `killall`, `recreate_collection`, `write_file` (overwrite), `pip uninstall`, `find -delete`, `git clean -fdx`, `dd of=` ### Design principles - **Memory-driven, not hardcoded**: Protection rules live in Qdrant, not in a static config - **Fail-open**: If Qdrant is unreachable, guardrails degrade to ALLOW (never block agent work by accident) - **Override with audit**: Explicit reasoning required, stored as private session memory for full audit trail ## Webhook Subscriptions MCP clients can register a webhook URL to be notified when the memory store changes. The server POSTs a small JSON payload to every subscriber that matches the event type — useful for keeping external systems (CRMs, audit logs, notification bots, second-brain tools) in sync with your memory. ### Event types | Event | Fires when | |-------|-----------| | `memory.remember` | A new memory has been stored (`remember` tool succeeded) | | `memory.update` | An existing memory was updated in place (`update` tool succeeded) | | `memory.forget` | A memory was deleted (`forget` tool succeeded) | ### Tools ```python # Register a webhook await mcp_nexus_subscribe( event_type="memory.remember", webhook_url="https://example.com/hooks/nexus" ) # → { "status": "subscribed", "subscription": { "id": "", ... } } # List all subscriptions await mcp_nexus_list_subscriptions() # → { "subscriptions": [...], "count": 1 } # Remove one await mcp_nexus_unsubscribe(subscription_id="") ``` ### Payload shape The server POSTs a JSON body to your URL: ```json { "event": "memory.remember", "memory_id": "", "timestamp": "2026-06-13T12:34:56+00:00" } ``` ### Storage Subscriptions are persisted in `~/.nexus-webhooks.json` (a single human-readable JSON file). They survive server restarts. No new Qdrant collection or database is required. ### Delivery semantics * **Fire-and-forget** — the tool call returns immediately; HTTP delivery happens in a background task. A slow or unresponsive endpoint does not block `remember` / `update` / `forget`. * **Best-effort, 5s timeout** — POSTs time out after 5 seconds. Failures (timeouts, 4xx, 5xx, DNS errors) are logged at WARNING and never crash the server. * **No retries** — webhook delivery is at-most-once. Subscribers should tolerate gaps; use `recall` to reconcile state if needed. ### Validation * `event_type` must be one of `memory.remember`, `memory.update`, `memory.forget`. Unknown values are rejected with an error envelope. * `webhook_url` must be a non-empty `http://` or `https://` URL. Other schemes (`ftp://`, `javascript:`, …) are rejected. ## Memory Categories (State-Prefixing) `category` is a **required** parameter on `remember` (declared in the tool schema). The server applies `"fact"` as a backward-compatible default if a client omits it or sends an unknown value, so older clients keep working. The seven scopes map to the State-Prefixing pattern from Agentic Design Patterns (Ch8): | Category | Scope | Lifetime / Notes | |----------|-------|------------------| | `fact` | Permanent verified facts | Default. No TTL, no drift check. | | `belief` | Mutable assumptions | Drift-detection candidates (nightly job). | | `session` | Session-specific | Episodic memory, scoped to a single session. | | `rule` | Operating rules | Stable, rarely changed. | | `preference` | User preferences | Per-user, durable. | | `temp` | Ephemeral | Expires after a TTL. | > **Legacy data:** memories stored before `category` was required are returned > with `category: "fact"` on read so consumers always see a valid enum value. ## Access Levels | Level | Description | Visible to | |-------|-------------|-----------| | `public` | General knowledge | All agents | | `trusted` | Personal data | Trusted agents (e.g. Kiosha) | | `private` | Sensitive data | Owner only (Nebo) | ## Provenance Provenance is **strongly recommended** for every memory you store. It lets the server verify sources at recall time (Rung 2: Justification Check) so you always know whether a memory's evidence is still alive. ### Recommended call ```python # Best practice: include source_url + confidence on every remember() await mcp_nexus_remember( text="Voyage-3-large produces 1024-dim embeddings", source_url="https://docs.voyageai.com/docs/embeddings", confidence=0.95, source="documentation", category="fact", ) ``` ### The three parameters | Parameter | Type | Default | Purpose | |-----------|------|---------|---------| | `source_url` | string | `""` | **Activates verification.** When set, the server runs an async HTTP HEAD against this URL on every recall. Result is returned as the `verification` field. Omit to disable verification. | | `confidence` | number 0.0-1.0 | `0.7` | Your self-assessed confidence in the fact. Use 0.9+ for verified facts, 0.5-0.8 for beliefs / inferences, <0.5 for speculative notes. | | `source` | string | `""` | Free-form origin label — `"conversation"`, `"document"`, `"cron"`, etc. Stored alongside, but does not trigger verification. | > **All three stay optional.** Older clients that omit them keep working — they > just get `verification: "unchecked"` on recall and `confidence: 0.7` stored. ### Justification Check (Rung 2) — what recall returns Each recall result includes a `verification` field derived from `source_url`: | Status | When | Meaning | |--------|------|---------| | `verified` | `source_url` set, HTTP HEAD returned `< 400` | The source is alive — treat the memory as trustworthy. | | `unreachable` | `source_url` set, HEAD failed (timeout, DNS, 4xx/5xx, bot block) | The source is gone or inaccessible — downgrade the memory's trust and consider re-fetching. | | `unchecked` | `source_url` was not set at storage time | No provenance was attached — the server did not verify anything. | Source URLs are checked in parallel via async HTTP HEAD on every recall. If a URL becomes unreachable, the agent sees the downgrade immediately and can decide whether to keep, refresh, or drop the memory. ### Why this matters - **Spivakovsky's Ladder of Checks, Rung 2:** *schema-valid is not answer-correct.* A memory that was true yesterday may be stale today. Justification verification keeps evidence alive without re-running the original ingest. - **Trust degrades visibly:** a fact with `verification: "unreachable"` is a signal to the agent to either re-check the source or treat the claim as provisional. - **Zero-config default:** when you cannot supply a `source_url`, omit it — no error, no warning, just `verification: "unchecked"`. ## Architecture ``` MCP Client ← stdio → nexus-memory (MCP Server) │ ┌──────┴──────┐ │ Qdrant │ │ localhost:6333 │ └──────┬──────┘ │ ┌────────────┼────────────┐ │ │ │ Voyage OpenAI Google (1024d) (1536d) (768d) │ │ │ Jina Ollama sentence- (1024d) (768d) transformer (384d) ``` > **Auto-detection:** The server tries Voyage → OpenAI → Google → Jina → Ollama (prefers bge-m3) → sentence-transformers. First available wins. No manual selection needed. ### Key Components - **nexus/** — core library (MemoryCategory, HybridRetriever, DriftDetector, Provenance, Lifecycle, Graph, Discovery, Export, ...) - **src/nexus_memory/mcp_server.py** — MCP server (5 tools, guardrails, access control) - **tests/** — 224 tests (pytest) ## Testing ```bash cd ~/nexus-memory pip install pytest pytest tests/ -v ``` ## Data All memories live in a single Qdrant collection called **`nexus`**: - **Memories, beliefs, events, documents** — all in one collection - **Hybrid search** — BM25 full-text + vector similarity + RRF re-ranking - **Access levels** — public / trusted / private (enforced by MCP tools) - **Categories** — fact, belief, session, rule, preference, procedure, temp > **Migration complete.** Old collections (`hermes-memory`, `openclaw-memory`, `nexus_beliefs`) have been consolidated into `nexus`. If you're migrating from a previous version, your data is already there — simply point your MCP client at the `nexus` collection. ## Release ```bash git tag v0.2.1 && git push --tags pip install build && python3 -m build && python3 -m twine upload dist/* ```