clawmem 0.13.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/AGENTS.md +165 -677
  2. package/CLAUDE.md +4 -747
  3. package/README.md +20 -191
  4. package/SKILL.md +157 -711
  5. package/docs/clawmem-architecture.excalidraw +2415 -0
  6. package/docs/clawmem-architecture.png +0 -0
  7. package/docs/clawmem_hero.jpg +0 -0
  8. package/docs/concepts/architecture.md +413 -0
  9. package/docs/concepts/composite-scoring.md +133 -0
  10. package/docs/concepts/hooks-vs-mcp.md +156 -0
  11. package/docs/concepts/multi-vault.md +71 -0
  12. package/docs/contributing.md +101 -0
  13. package/docs/guides/cloud-embedding.md +134 -0
  14. package/docs/guides/hermes-plugin.md +187 -0
  15. package/docs/guides/inference-services.md +144 -0
  16. package/docs/guides/multi-vault-config.md +84 -0
  17. package/docs/guides/openclaw-plugin.md +306 -0
  18. package/docs/guides/setup-hooks.md +146 -0
  19. package/docs/guides/setup-mcp.md +76 -0
  20. package/docs/guides/systemd-services.md +332 -0
  21. package/docs/guides/upgrading.md +566 -0
  22. package/docs/internals/entity-resolution.md +135 -0
  23. package/docs/internals/graph-traversal.md +85 -0
  24. package/docs/internals/intent-search-pipeline.md +103 -0
  25. package/docs/internals/query-pipeline.md +100 -0
  26. package/docs/introduction.md +104 -0
  27. package/docs/quickstart.md +158 -0
  28. package/docs/reference/cli.md +195 -0
  29. package/docs/reference/configuration.md +101 -0
  30. package/docs/reference/mcp-tools.md +336 -0
  31. package/docs/reference/rest-api.md +204 -0
  32. package/docs/troubleshooting.md +330 -0
  33. package/package.json +2 -1
  34. package/src/clawmem.ts +60 -0
  35. package/src/health/rerank-golden.json +54 -0
  36. package/src/health/rerank-health.ts +150 -0
  37. package/src/mcp.ts +20 -1
  38. package/src/memory.ts +2 -0
  39. package/src/search-utils.ts +35 -4
  40. package/src/store.ts +115 -22
@@ -0,0 +1,187 @@
1
+ # Hermes Agent MemoryProvider plugin
2
+
3
+ ClawMem integrates with Hermes Agent as a native MemoryProvider plugin, giving Hermes agents the same persistent memory available to Claude Code and OpenClaw. All three runtimes share a single vault, so decisions captured in one are available in the others.
4
+
5
+ ## Install
6
+
7
+ Hermes scans two directories for memory provider plugins (since Hermes #10529, in v2026.4.13+):
8
+
9
+ 1. **User plugins** at `$HERMES_HOME/plugins/<name>/` — typically `~/.hermes/plugins/<name>/`. **Preferred.** Survives `git pull` of hermes-agent and avoids the dual-registration trap that previously caused duplicate tool names with strict providers.
10
+ 2. **Bundled plugins** at `hermes-agent/plugins/memory/<name>/` — always supported. Bundled-first precedence on name collisions.
11
+
12
+ ```bash
13
+ # Preferred — user-plugin path
14
+ cp -r /path/to/ClawMem/src/hermes ${HERMES_HOME:-~/.hermes}/plugins/clawmem
15
+
16
+ # Or symlink for development (either path)
17
+ ln -s /path/to/ClawMem/src/hermes ${HERMES_HOME:-~/.hermes}/plugins/clawmem
18
+
19
+ # Bundled-style — only when working in the hermes-agent source tree
20
+ cp -r /path/to/ClawMem/src/hermes /path/to/hermes-agent/plugins/memory/clawmem
21
+ ```
22
+
23
+ Discovery is heuristic — Hermes looks for `register_memory_provider` or `MemoryProvider` substrings in `__init__.py`. Both are present in `src/hermes/__init__.py`, so the plugin is discovered correctly under either path.
24
+
25
+ Activate via `memory.provider: clawmem` in `~/.hermes/config.yaml` (or run `hermes memory setup` and pick `clawmem`). Memory providers are an exclusive category — exactly one is active at a time, selected via `memory.provider`, completely separate from the general plugin loader.
26
+
27
+ > **Do NOT add `clawmem` to `plugins.enabled` in `config.yaml`.** That list is the general-plugin opt-in roster (Hermes #11xxx onwards made all general plugins opt-in by default). Memory providers have their own activation channel via `memory.provider` and the general loader explicitly skips bundled `plugins/memory/` and treats user-installed memory providers as separate from the standalone-plugin gate. Adding `clawmem` to `plugins.enabled` would make the general loader try to import it as a `kind: standalone` plugin and call `register(ctx)` against the general `PluginContext` — which doesn't expose `register_memory_provider`, so the import errors and the warning gets logged. Harmless but noisy.
28
+
29
+ Verify discovery:
30
+ ```bash
31
+ hermes memory list # Should show "clawmem" as available
32
+ ```
33
+
34
+ ## Architecture
35
+
36
+ The plugin uses shell-out for lifecycle hooks and REST API for interactive tools:
37
+
38
+ | Component | Transport | Role |
39
+ |-----------|-----------|------|
40
+ | `initialize()` | Shell-out | Create transcript, run `session-bootstrap`, cache bootstrap context |
41
+ | `prefetch()` / `queue_prefetch()` | Shell-out | Prompt-aware retrieval via `context-surfacing` hook (automatic every turn) |
42
+ | `sync_turn()` | Local file I/O | Append user+assistant to plugin-managed transcript JSONL |
43
+ | `on_session_end()` | Shell-out | `decision-extractor`, `handoff-generator`, `feedback-loop` in parallel |
44
+ | `on_pre_compress()` | Shell-out | `precompact-extract` for state preservation |
45
+ | `system_prompt_block()` | In-process | Static provider info and tool names |
46
+ | Agent tools (5) | REST API | `clawmem_retrieve`, `clawmem_get`, `clawmem_session_log`, `clawmem_timeline`, `clawmem_similar` |
47
+
48
+ ### Why shell-out for hooks?
49
+
50
+ ClawMem's lifecycle hooks (context-surfacing, decision-extractor, etc.) are Bun/TypeScript programs that read a transcript JSONL file and interact with the SQLite vault directly. The Python plugin shells out to the `clawmem` binary to invoke them, avoiding a cross-language library dependency. This is the same pattern used by the OpenClaw plugin.
51
+
52
+ ### Why REST for tools?
53
+
54
+ Interactive tool calls need structured JSON responses and benefit from the REST server's connection pooling. The `clawmem serve` process stays warm, so tool calls complete in milliseconds.
55
+
56
+ ### Plugin-managed transcript
57
+
58
+ Hermes passes turn data via `sync_turn(user_content, assistant_content)`, but ClawMem hooks expect a `.jsonl` transcript file. The plugin bridges this by maintaining its own transcript at `$HERMES_HOME/clawmem-transcripts/<session_id>.jsonl`, appending each turn in Claude Code transcript format (`{"type":"message","message":{"role":"...","content":"..."}}`).
59
+
60
+ ## Configuration
61
+
62
+ Set in your Hermes profile's `.env` or shell environment:
63
+
64
+ | Variable | Default | Description |
65
+ |----------|---------|-------------|
66
+ | `CLAWMEM_BIN` | auto-detect on PATH | Path to `clawmem` binary |
67
+ | `CLAWMEM_SERVE_PORT` | `7438` | REST API port |
68
+ | `CLAWMEM_SERVE_MODE` | `external` | `external` (you run `clawmem serve`) or `managed` (plugin starts/stops it) |
69
+ | `CLAWMEM_PROFILE` | `balanced` | Retrieval profile: `speed` (BM25 only), `balanced` (hybrid), `deep` (full pipeline) |
70
+ | `CLAWMEM_EMBED_URL` | — | GPU embedding server URL (e.g., `http://localhost:8088`) |
71
+ | `CLAWMEM_LLM_URL` | — | GPU LLM server URL (e.g., `http://localhost:8089`) |
72
+ | `CLAWMEM_LLM_MODEL` | `qwen3` | Model name sent to the GPU/cloud LLM endpoint (e.g., `qwen3`, `gpt-5.4-mini`) |
73
+ | `CLAWMEM_LLM_REASONING_EFFORT` | — | Optional top-level `reasoning_effort` field for Chat Completions endpoints that support it (for example OpenAI reasoning models). Leave unset for llama-server/vLLM unless explicitly supported. |
74
+ | `CLAWMEM_LLM_NO_THINK` | `true` | Append `/no_think` to remote prompts; set to `false` for standard OpenAI models and other endpoints that reject or treat the Qwen-style suffix as literal prompt text |
75
+ | `CLAWMEM_RERANK_URL` | — | GPU reranker server URL (e.g., `http://localhost:8090`) |
76
+ | `CLAWMEM_API_TOKEN` | — | Bearer token for REST API auth (optional, must match `clawmem serve` config) |
77
+
78
+ Or configure interactively:
79
+ ```bash
80
+ hermes memory setup # Walks through provider configuration
81
+ ```
82
+
83
+ ## Server modes
84
+
85
+ ### External (recommended for production)
86
+
87
+ You manage `clawmem serve` yourself, either as a systemd service or a background process:
88
+
89
+ ```bash
90
+ clawmem serve --port 7438 &
91
+ # or via systemd — see docs/guides/systemd-services.md
92
+ ```
93
+
94
+ The plugin connects to the existing server. If the server is unreachable, tools fail gracefully but hooks still work (shell-out transport).
95
+
96
+ ### Managed
97
+
98
+ The plugin starts `clawmem serve` during `initialize()` and stops it on `shutdown()`. Includes a readiness probe (5s health check loop) and early-exit detection.
99
+
100
+ ```bash
101
+ export CLAWMEM_SERVE_MODE=managed
102
+ ```
103
+
104
+ Suitable for development. Not recommended for production — the process doesn't survive plugin crashes or Hermes restarts.
105
+
106
+ ## Agent-context isolation
107
+
108
+ Hermes's `run_agent.py` passes an `agent_context` kwarg to every `MemoryProvider.initialize()` call with one of four values: `"primary"`, `"subagent"`, `"cron"`, or `"flush"`. The `MemoryProvider` ABC docstring is explicit about why this matters: *"Providers should skip writes for non-primary contexts (cron system prompts would corrupt user representations)."*
109
+
110
+ The plugin honours this contract by gating only the **write-side** surfaces — read-side hooks always run so non-primary agents still benefit from retrieval:
111
+
112
+ | Surface | Direction | `agent_context != "primary"` behaviour |
113
+ |---|---|---|
114
+ | `session-bootstrap` (in `initialize`) | Read | Runs — context still surfaced |
115
+ | `prefetch()` / `queue_prefetch()` (`context-surfacing`) | Read | Runs — context still surfaced |
116
+ | `system_prompt_block()` | Read | Runs — provider info still injected |
117
+ | Agent tools (REST) | Read | Runs — agents can still call `clawmem_retrieve` etc. |
118
+ | `sync_turn()` (transcript append) | Write | **Suppressed** |
119
+ | `on_session_end()` (extraction) | Write | **Suppressed** |
120
+ | `on_pre_compress()` (precompact) | Write | **Suppressed** |
121
+
122
+ Net effect: subagents, cron jobs, and flush passes get the benefit of vault recall without contaminating the vault with intermediate state or system-prompt reasoning. The `initialize()` log line records the active context for operator visibility:
123
+
124
+ ```
125
+ clawmem: agent_context=cron — reads enabled, writes suppressed
126
+ ```
127
+
128
+ ## Hermes built-in memory coexistence
129
+
130
+ Hermes always runs its built-in memory provider (MEMORY.md / USER.md) alongside the external provider. ClawMem is additive — it does not replace or disable built-in memory. Both inject into the context independently.
131
+
132
+ This means some duplication is possible (built-in memory captures a fact, ClawMem extracts the same fact from the transcript). In practice the overlap is minimal because:
133
+ - Built-in memory captures explicit `add_to_memory` tool calls
134
+ - ClawMem captures implicit decisions, handoffs, and patterns from conversation flow
135
+ - Different storage formats (markdown files vs SQLite vault) serve different retrieval strategies
136
+
137
+ `on_memory_write()` is intentionally a no-op in v1 to avoid amplifying duplication.
138
+
139
+ ## Shared vault across frameworks
140
+
141
+ Claude Code, OpenClaw, and Hermes all access the same SQLite vault file (`~/.cache/clawmem/index.sqlite` by default). A decision captured in a Claude Code session is visible to Hermes agents, and vice versa.
142
+
143
+ SQLite WAL mode + `busy_timeout=5000ms` handles concurrent access. The plugin-managed transcript is stored separately under `$HERMES_HOME/clawmem-transcripts/` and does not affect the shared vault.
144
+
145
+ ## What needs to be running
146
+
147
+ | Service | Purpose | Managed by |
148
+ |---------|---------|------------|
149
+ | `clawmem serve` | REST API for agent tools | External (systemd) or managed (plugin) |
150
+ | `clawmem-watcher` | Auto-index on file changes | [systemd](systemd-services.md#watcher-service) |
151
+ | `clawmem-embed.timer` | Daily embedding sweep | [systemd](systemd-services.md#embed-timer) |
152
+ | GPU servers (optional) | Embedding, LLM, reranker | [systemd](systemd-services.md#gpu-service-units) or in-process fallback |
153
+
154
+ ## Verify
155
+
156
+ ```bash
157
+ # Plugin discovered
158
+ hermes memory list | grep clawmem
159
+
160
+ # REST API responding
161
+ curl http://localhost:7438/health
162
+
163
+ # Hooks working
164
+ clawmem status
165
+
166
+ # Watcher active
167
+ systemctl --user status clawmem-watcher.service
168
+ ```
169
+
170
+ ## Lifecycle mapping reference
171
+
172
+ | Hermes MemoryProvider | ClawMem equivalent | Notes |
173
+ |---|---|---|
174
+ | `is_available()` | PATH check for `clawmem` binary | No network calls |
175
+ | `initialize(session_id, **kwargs)` | `session-bootstrap` hook | Creates transcript, caches bootstrap context. Reads `agent_context` and `hermes_home` from kwargs. Other kwargs Hermes passes (`platform`, `agent_identity`, `agent_workspace`, `parent_session_id`, `user_id`, `gateway_session_key`, `session_title`) are absorbed via `**kwargs` and currently unused. |
176
+ | `system_prompt_block()` | Static text | Provider active, tool names |
177
+ | `prefetch(query)` | `context-surfacing` hook output | Returns cached result from background thread |
178
+ | `queue_prefetch(query)` | `context-surfacing` hook | Background thread, generation-safe |
179
+ | `sync_turn(user, assistant)` | Transcript JSONL append | Bridges Hermes turn pairs to ClawMem file format. Suppressed when `agent_context != "primary"`. |
180
+ | `on_turn_start()` | — | Not overridden — base no-op |
181
+ | `on_session_end(messages)` | `decision-extractor` + `handoff-generator` + `feedback-loop` | Parallel, 30s timeout each. Suppressed when `agent_context != "primary"`. |
182
+ | `on_pre_compress(messages)` | `precompact-extract` | Side effect only (Hermes ignores return). Suppressed when `agent_context != "primary"`. |
183
+ | `on_memory_write()` | No-op | Avoids duplication with built-in memory (filesystem watcher already indexes MEMORY.md / USER.md if they live under a configured collection). |
184
+ | `on_delegation()` | No-op | Subagent observation handled at the parent's primary context already; nothing useful to add here. |
185
+ | `get_tool_schemas()` | 5 REST-backed tools | retrieve, get, session_log, timeline, similar |
186
+ | `handle_tool_call()` | REST API dispatch | Bearer auth when `CLAWMEM_API_TOKEN` is set |
187
+ | `shutdown()` | Thread cleanup + managed serve stop | Joins prefetch thread, terminates managed process |
@@ -0,0 +1,144 @@
1
+ # Inference services — choosing and running your stack
2
+
3
+ ClawMem uses three inference services: **embedding**, **LLM** (query expansion / intent classification / A-MEM enrichment), and **reranker** (cross-encoder). In the **default** stack all three run as `llama-server` (llama.cpp) instances, each with an in-process `node-llama-cpp` fallback that auto-downloads on first use — so ClawMem works with no manual setup and no dedicated GPU. The `bin/clawmem` wrapper points the three endpoint vars at `localhost:8088` (embedding), `localhost:8089` (LLM), `localhost:8090` (reranker) by default.
4
+
5
+ > **Always run ClawMem via the `bin/clawmem` wrapper.** It exports the endpoint defaults. Invoking `bun run src/clawmem.ts` directly skips them and silently falls back to in-process CPU inference (slow). For remote GPU, add the same vars to your systemd units — see [systemd-services.md](systemd-services.md).
6
+
7
+ ## Choosing your inference stack
8
+
9
+ Three stacks, picked by hardware, license, and quality needs. This is the decision; the rest of this guide is how to run each.
10
+
11
+ | Stack | Models | VRAM | License | Retrieval quality / context | Pick when |
12
+ |---|---|---|---|---|---|
13
+ | **QMD native** (default) | EmbeddingGemma-300M (768d) + qmd-query-expansion-1.7B + qwen3-reranker-0.6B | ~4 GB total, or **in-process** (Metal/Vulkan/CPU) | **Permissive — commercial OK** | Good · 2K embed context | Any GPU **or no GPU**; **commercial use**; zero-config start (auto-downloads) |
14
+ | **z / SOTA** | zembed-1 (2560d, zELO-distilled from zerank-2) + qmd-query-expansion-1.7B + zerank-2 seq-cls **sidecar** (bf16) | ~16 GB (4.4 + 2.2 + 9) | **CC-BY-NC-4.0 — non-commercial only** | Best (zerank-2 NDCG@10 ahead of Cohere rerank-3.5) · 32K embed context | 16 GB+ GPU **and** non-commercial; want top recall |
15
+ | **Cloud embedding** | Jina v5-text-small (1024d, rec.) / OpenAI / Voyage / Cohere — **embedding only** | none (embedding) | provider ToS | provider-dependent · up to 128K (Cohere) | No local GPU for embedding, or prefer managed. **LLM + reranker still run local/in-process.** |
16
+
17
+ **Decision axes:** VRAM budget · license (commercial vs non-commercial) · retrieval quality · context length. The default native stack is the right starting point for most users and the only stack with no licensing restriction; upgrade to the z-stack only with a 16 GB+ GPU and a non-commercial use case; use cloud embedding when you have no local GPU to spare for embeddings.
18
+
19
+ ## Landmines (read before serving)
20
+
21
+ - **The zerank-2 GGUF is deprecated and inert.** llama.cpp's converter drops zerank's CrossEncoder/LogitScore head, so under `--reranking` it returns HTTP 200 with near-zero, non-discriminating scores — the final ordering silently collapses to RRF. Serve the SOTA reranker via the **seq-cls sidecar** (`extras/rerankers/zerank-2-seq/`), never as a GGUF. Run `clawmem rerank-health` to confirm a reranker actually discriminates (liveness ≠ correctness).
22
+ - **`-ub` must equal `-b`** for embedding/reranking models (non-causal attention) or `llama-server` asserts (`non-causal attention requires n_ubatch >= n_tokens`). The zerank-2 sidecar is transformers-served and exempt; the qwen3-reranker GGUF does not need it. See [llama.cpp#12836](https://github.com/ggml-org/llama.cpp/issues/12836).
23
+ - **Changing embedding dimensions requires a full re-embed:** `clawmem embed --force` (idempotent, safe to interrupt/resume).
24
+ - **Set `CLAWMEM_NO_LOCAL_MODELS=true`** for remote-only / dedicated-server setups to fail fast on an unreachable endpoint instead of silently auto-downloading multi-GB GGUFs and running CPU inference.
25
+
26
+ ## Default stack — QMD native (any GPU or in-process)
27
+
28
+ Total ~4 GB VRAM, or runs in-process via `node-llama-cpp` (Metal on Apple Silicon, Vulkan where available, CPU as last resort — fast with GPU acceleration, significantly slower CPU-only). All three auto-download on first use if no server is running.
29
+
30
+ | Service | Port | Model | VRAM | Purpose |
31
+ |---|---|---|---|---|
32
+ | Embedding | 8088 | [EmbeddingGemma-300M-Q8_0](https://huggingface.co/ggml-org/embeddinggemma-300M-GGUF) (314 MB, 768d, 2K ctx) | ~400 MB | Vector search, indexing, context-surfacing |
33
+ | LLM | 8089 | [qmd-query-expansion-1.7B-q4_k_m](https://huggingface.co/tobil/qmd-query-expansion-1.7B-gguf) (~1.1 GB) | ~2.2 GB | Intent classification, query expansion, A-MEM |
34
+ | Reranker | 8090 | [qwen3-reranker-0.6B-Q8_0](https://huggingface.co/ggml-org/Qwen3-Reranker-0.6B-Q8_0-GGUF) (~600 MB) | ~1.3 GB | Cross-encoder reranking |
35
+
36
+ ```bash
37
+ # Embedding (--embeddings flag required)
38
+ llama-server -m embeddinggemma-300M-Q8_0.gguf \
39
+ --embeddings --port 8088 --host 0.0.0.0 -ngl 99 -c 2048 --batch-size 2048
40
+
41
+ # LLM (QMD finetuned model)
42
+ llama-server -m qmd-query-expansion-1.7B-q4_k_m.gguf \
43
+ --port 8089 --host 0.0.0.0 -ngl 99 -c 4096 --batch-size 512
44
+
45
+ # Reranker
46
+ llama-server -m Qwen3-Reranker-0.6B-Q8_0.gguf \
47
+ --reranking --port 8090 --host 0.0.0.0 -ngl 99 -c 2048 --batch-size 512
48
+ ```
49
+
50
+ On CPU, omit `-ngl 99`. If a server is unreachable (ECONNREFUSED/ETIMEDOUT), ClawMem sets a 60-second cooldown and falls back to in-process inference; HTTP 4xx/5xx and user-cancelled requests do not trigger cooldown.
51
+
52
+ ## SOTA stack — z models (16 GB+ GPU, CC-BY-NC-4.0, non-commercial only)
53
+
54
+ ZeroEntropy's distillation-paired stack — best retrieval quality, total ~16 GB VRAM. zembed-1 is distilled from zerank-2 via [zELO](https://docs.zeroentropy.dev), so the pair is mutually optimal.
55
+
56
+ | Service | Port | Model | VRAM | Purpose |
57
+ |---|---|---|---|---|
58
+ | Embedding | 8088 | [zembed-1-Q4_K_M](https://huggingface.co/Abhiray/zembed-1-Q4_K_M-GGUF) (2.4 GB, 2560d, 32K ctx) | ~4.4 GB | SOTA embedding |
59
+ | LLM | 8089 | qmd-query-expansion-1.7B-q4_k_m | ~2.2 GB | (same as default) |
60
+ | Reranker | 8090 | [zerank-2 seq-cls sidecar](../../extras/rerankers/zerank-2-seq/) (transformers, bf16) | ~9 GB | SOTA reranker — **not** a GGUF |
61
+
62
+ ```bash
63
+ # Embedding (zembed-1) — -ub MUST equal -b for non-causal attention
64
+ llama-server -m zembed-1-Q4_K_M.gguf \
65
+ --embeddings --port 8088 --host 0.0.0.0 -ngl 99 -c 8192 -b 2048 -ub 2048
66
+
67
+ # Reranker (zerank-2) — seq-cls SIDECAR (transformers, bf16), NOT a llama-server GGUF:
68
+ cd extras/rerankers/zerank-2-seq
69
+ docker compose build
70
+ HF_TOKEN=hf_xxx docker compose run --rm convert # download + convert + verify (all gates must pass)
71
+ docker compose up -d reranker # serves /v1/rerank on :8090
72
+ ```
73
+
74
+ ## Embedding (detail)
75
+
76
+ ClawMem calls the OpenAI-compatible `/v1/embeddings` endpoint for all embedding operations — works with local `llama-server` and cloud providers alike.
77
+
78
+ - **GPU with VRAM to spare:** zembed-1 (Option above) — SOTA, multilingual out of the box.
79
+ - **No GPU / limited VRAM:** EmbeddingGemma-300M-Q8_0 (Option above). For a lightweight multilingual alternative use [granite-embedding-278m-multilingual-Q6_K](https://huggingface.co/bartowski/granite-embedding-278m-multilingual-GGUF) (314 MB; set `CLAWMEM_EMBED_MAX_CHARS=1100` for its 512-token context).
80
+ - **Cloud:** any OpenAI-compatible `/v1/embeddings` provider — Jina (recommended `jina-embeddings-v5-text-small`, 1024d), OpenAI, Voyage, Cohere. Full provider matrix, batch/TPM behavior, and per-provider params are in [cloud-embedding.md](cloud-embedding.md).
81
+
82
+ ```bash
83
+ # Verify an embedding endpoint is reachable
84
+ curl $CLAWMEM_EMBED_URL/v1/embeddings \
85
+ -H "Content-Type: application/json" \
86
+ -H "Authorization: Bearer $CLAWMEM_EMBED_API_KEY" \
87
+ -d "{\"input\":\"test\",\"model\":\"$CLAWMEM_EMBED_MODEL\"}"
88
+ ```
89
+
90
+ ## LLM server
91
+
92
+ Intent classification, query expansion, and A-MEM extraction use [qmd-query-expansion-1.7B](https://huggingface.co/tobil/qmd-query-expansion-1.7B-gguf) — a Qwen3-1.7B finetuned by QMD for generating search-expansion terms (hyde, lexical, vector variants). ~1.1 GB at q4_k_m, served on port 8089. If `CLAWMEM_LLM_URL` is unset, `node-llama-cpp` auto-downloads it.
93
+
94
+ - **Performance (RTX 3090):** intent classification ~27 ms; query expansion ~333 tok/s; VRAM ~2.2–2.8 GB.
95
+ - **Qwen3 `/no_think`:** Qwen3 emits thinking tokens by default; ClawMem appends `/no_think` to all prompts automatically for structured output.
96
+ - **Dual-path intent:** a heuristic regex classifier handles strong why/when/who signals instantly (0.8+ confidence); the LLM refines only ambiguous queries below that threshold.
97
+
98
+ ```bash
99
+ llama-server -m qmd-query-expansion-1.7B-q4_k_m.gguf \
100
+ --port 8089 --host 0.0.0.0 -ngl 99 -c 4096 --batch-size 512
101
+ ```
102
+
103
+ For better entity-extraction quality during `reindex --enrich`, point `CLAWMEM_LLM_URL` at a 7B+ model or cloud API (see [../internals/entity-resolution.md](../internals/entity-resolution.md)).
104
+
105
+ ## Reranker server
106
+
107
+ Cross-encoder reranking for the `query` (4000-char context, deep) and `intent_search` (200-char context, fast) pipelines on port 8090, via the `/v1/rerank` endpoint.
108
+
109
+ - **GPU with VRAM to spare:** the zerank-2 seq-cls sidecar (recipe above). **CC-BY-NC-4.0.**
110
+ - **CPU / limited VRAM:** qwen3-reranker-0.6B-Q8_0 (~600 MB, ~1.3 GB VRAM), the QMD native reranker — auto-downloaded if no server is running.
111
+
112
+ ```bash
113
+ llama-server -m Qwen3-Reranker-0.6B-Q8_0.gguf \
114
+ --reranking --port 8090 --host 0.0.0.0 -ngl 99 -c 2048 --batch-size 512
115
+ ```
116
+
117
+ See the landmines above: the zerank-2 **GGUF is inert** (use the sidecar), and verify discrimination with `clawmem rerank-health`.
118
+
119
+ ## Remote GPU
120
+
121
+ If the GPU lives on a separate machine, point the env vars at it and disable local fallback:
122
+
123
+ ```bash
124
+ export CLAWMEM_EMBED_URL=http://gpu-host:8088
125
+ export CLAWMEM_LLM_URL=http://gpu-host:8089
126
+ export CLAWMEM_LLM_MODEL=qwen3
127
+ export CLAWMEM_RERANK_URL=http://gpu-host:8090
128
+ export CLAWMEM_NO_LOCAL_MODELS=true # fail fast instead of auto-downloading multi-GB GGUFs
129
+ ```
130
+
131
+ ## Verify endpoints
132
+
133
+ ```bash
134
+ curl http://host:8088/v1/embeddings -d '{"input":"test","model":"embedding"}' -H 'Content-Type: application/json'
135
+ curl http://host:8089/v1/models
136
+ curl http://host:8090/v1/models
137
+ ```
138
+
139
+ ## See also
140
+
141
+ - **Cloud embedding** — provider matrix, batch embedding, TPM-aware pacing, per-provider params → [cloud-embedding.md](cloud-embedding.md)
142
+ - **All environment variables** (endpoints, profiles, consolidation, merge gates) → [../reference/configuration.md](../reference/configuration.md)
143
+ - **Keeping servers up** (systemd units, GPU env in services) → [systemd-services.md](systemd-services.md)
144
+ - **Reranker health** — the degenerate-reranker failure mode and `clawmem rerank-health` → [../troubleshooting.md](../troubleshooting.md)
@@ -0,0 +1,84 @@
1
+ # Multi-vault configuration
2
+
3
+ Step-by-step guide for setting up multiple independent ClawMem vaults to isolate AI agent memory by project or team.
4
+
5
+ ## 1. Define vaults
6
+
7
+ Edit `~/.config/clawmem/config.yaml`:
8
+
9
+ ```yaml
10
+ vaults:
11
+ work: ~/.cache/clawmem/work.sqlite
12
+ personal: ~/.cache/clawmem/personal.sqlite
13
+ research: ~/data/research-vault.sqlite
14
+ ```
15
+
16
+ Or use the environment variable:
17
+
18
+ ```bash
19
+ export CLAWMEM_VAULTS='{"work":"~/.cache/clawmem/work.sqlite","personal":"~/.cache/clawmem/personal.sqlite"}'
20
+ ```
21
+
22
+ Paths support `~` expansion. Environment variables override config file values.
23
+
24
+ ## 2. Populate vaults
25
+
26
+ Use `vault_sync` via MCP or CLI:
27
+
28
+ ```
29
+ vault_sync(vault="work", content_root="~/projects/work-notes")
30
+ vault_sync(vault="personal", content_root="~/notes/personal")
31
+ ```
32
+
33
+ Or add collections to a vault manually:
34
+
35
+ ```bash
36
+ INDEX_PATH=~/.cache/clawmem/work.sqlite ./bin/clawmem collection add ~/projects/work-notes --name work-notes
37
+ INDEX_PATH=~/.cache/clawmem/work.sqlite ./bin/clawmem update --embed
38
+ ```
39
+
40
+ ## 3. Embed vault content
41
+
42
+ Each vault needs its own embedding pass:
43
+
44
+ ```bash
45
+ INDEX_PATH=~/.cache/clawmem/work.sqlite ./bin/clawmem embed
46
+ INDEX_PATH=~/.cache/clawmem/personal.sqlite ./bin/clawmem embed
47
+ ```
48
+
49
+ ## 4. Query vaults
50
+
51
+ All MCP tools accept `vault`:
52
+
53
+ ```
54
+ # Search work vault
55
+ query("API authentication flow", vault="work", compact=true)
56
+
57
+ # Check personal vault lifecycle
58
+ lifecycle_status(vault="personal")
59
+
60
+ # Pin something in research vault
61
+ memory_pin("important finding", vault="research")
62
+ ```
63
+
64
+ ## 5. List configured vaults
65
+
66
+ ```
67
+ list_vaults()
68
+ ```
69
+
70
+ Returns:
71
+
72
+ ```
73
+ Configured vaults (2):
74
+ work: /home/user/.cache/clawmem/work.sqlite
75
+ personal: /home/user/.cache/clawmem/personal.sqlite
76
+ ```
77
+
78
+ ## Notes
79
+
80
+ - The default (unnamed) vault at `~/.cache/clawmem/index.sqlite` always exists and is used when `vault` is omitted
81
+ - Hooks always operate on the default vault
82
+ - Named vault stores are cached in memory and reused across tool calls
83
+ - Each vault is fully independent — separate documents, embeddings, graphs, and sessions
84
+ - `vault_sync` validates paths against a deny-list (rejects `/etc/`, `.ssh`, `.env`, `credentials`, etc.)