clawmem 0.20.2 → 0.21.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.
package/AGENTS.md CHANGED
@@ -59,7 +59,7 @@ clawmem doctor # full health check (clawmem status
59
59
  | `feedback-loop` | Stop | tracks referenced notes → confidence boosts, co-activations, utility signals |
60
60
 
61
61
  **Default behavior:** read injected `<vault-context>` first; if sufficient, answer immediately.
62
- **Blind spots (by design):** hooks filter `_clawmem/` artifacts, enforce score thresholds, cap token budget — **absence in `<vault-context>` does NOT mean absence in memory.** If expected memory wasn't surfaced, escalate to Tier 3.
62
+ **Blind spots (by design):** hooks filter `_clawmem/` artifacts, enforce score thresholds, cap token budget — **absence in `<vault-context>` does NOT mean absence in memory.** If expected memory wasn't surfaced, escalate to Tier 3. Note the MCP retrieval tools themselves exclude `_clawmem` by default since v0.21.0 — pass `includeInternal: true` when system-internal memory (observations/handoffs/deductions) is the target.
63
63
  **Profiles:** `speed` / `balanced` (default) / `deep` set the kept-score ratio (65% / 55% / 45%) + an activation floor; only `deep` adds query expansion + reranking to the hook path. → [docs/concepts/hooks-vs-mcp.md](docs/concepts/hooks-vs-mcp.md).
64
64
 
65
65
  ### Tier 3 — agent-initiated (one targeted MCP call)
package/README.md CHANGED
@@ -531,6 +531,8 @@ clawmem status Quick index status
531
531
 
532
532
  Registered by `clawmem setup mcp`. Available to any MCP-compatible client.
533
533
 
534
+ **Internal-collection visibility (v0.21.0):** the retrieval tools `search`, `vsearch`, `query`, `query_plan`, `memory_retrieve`, and `find_similar` exclude the system-internal `_clawmem` collection by default — pass `includeInternal: true` (or name `_clawmem` in an explicit `collection` filter where the tool has one) to include it. `find_similar` auto-includes internal neighbors when the reference document is itself internal; `intent_search`, `find_causal_links`, `kg_query`, `session_log`, and `timeline` are unfiltered by design. Full contract (including the `degraded` under-fill markers): [docs/reference/mcp-tools.md](docs/reference/mcp-tools.md).
535
+
534
536
  | Tool | Description |
535
537
  |---|---|
536
538
  | `__IMPORTANT` | Workflow guide: prefer `memory_retrieve` → match query type to tool → `multi_get` for full content |
@@ -789,6 +791,7 @@ Notes referenced by the agent during a session get boosted (`access_count++`). U
789
791
  | `CLAWMEM_RERANK_URL` | `http://localhost:8090` | Reranker server URL. Without it, falls to `node-llama-cpp` (if allowed). |
790
792
  | `CLAWMEM_RERANK_API_KEY` | (none) | Bearer token for an authenticated remote reranker endpoint. Independent of the embed and LLM keys. |
791
793
  | `CLAWMEM_NO_LOCAL_MODELS` | `false` | Block `node-llama-cpp` from auto-downloading GGUF models. Set `true` for remote-only setups where you want fail-fast on unreachable endpoints. |
794
+ | `CLAWMEM_MCP_DIRECT_TUNED_WEIGHTS` | `false` | **v0.21.0.** When `true`, the MCP direct tools (`search`, `vsearch`, `memory_retrieve`) score non-recency queries with the retrieval-tuned `query`-tool weights. Ships off — the eval evidence covered only the hybrid `query` pipeline. YAML equivalent: `retrieval.mcp_direct_tuned_weights` (env wins). |
792
795
  | `CLAWMEM_MERGE_SCORE_NORMAL` | `0.93` | **v0.7.1.** Phase 2 consolidation merge-safety threshold when candidate and existing anchors align. Merges above this normalized 3-gram cosine score are allowed. |
793
796
  | `CLAWMEM_MERGE_SCORE_STRICT` | `0.98` | **v0.7.1.** Strictest merge-safety threshold — fallback when anchor sets are ambiguous. |
794
797
  | `CLAWMEM_MERGE_GUARD_DRY_RUN` | `false` | **v0.7.1.** When `true`, Phase 2 merge-safety rejections are logged but not enforced — use for calibration before enabling the gate. |
package/SKILL.md CHANGED
@@ -35,7 +35,7 @@ Hooks handle ~90% of retrieval at zero agent effort.
35
35
 
36
36
  **Default behavior:** read injected `<vault-context>` first; if sufficient, answer immediately.
37
37
 
38
- **Hook blind spots (by design):** hooks filter `_clawmem/` artifacts, enforce score thresholds, and cap token budget — **absence in `<vault-context>` does NOT mean absence in memory.** If expected memory wasn't surfaced, escalate to Tier 3.
38
+ **Hook blind spots (by design):** hooks filter `_clawmem/` artifacts, enforce score thresholds, and cap token budget — **absence in `<vault-context>` does NOT mean absence in memory.** If expected memory wasn't surfaced, escalate to Tier 3. Note the MCP retrieval tools themselves exclude `_clawmem` by default since v0.21.0 — pass `includeInternal: true` when system-internal memory (observations/handoffs/deductions) is the target.
39
39
 
40
40
  **Profiles:** `speed` / `balanced` (default) / `deep` set the kept-score ratio (65% / 55% / 45%) and an activation floor. Only `deep` adds query expansion + reranking to the hook path. Profile and the hook `timeout` are set in `~/.claude/settings.json` — see *Operational gotchas* for timeout tuning.
41
41
 
@@ -19,7 +19,9 @@ compositeScore = (w_search * searchScore + w_recency * recencyScore + w_confiden
19
19
 
20
20
  Recency intent is detected automatically when queries contain "latest", "recent", "last session", etc. — and **takes precedence over the `query`-tool weights**: a recency-phrased `query` call still uses the Recency-intent column.
21
21
 
22
- The **`query` tool** uses retrieval-tuned weights (search 0.70) derived from a held-out judged-relevance eval (v0.13.0) — more weight on topical relevance measurably improves graded NDCG@10 without demoting the newest-correct version of evolving docs. All other tools (`search`, `vsearch`, `memory_retrieve`, and the context-surfacing hook) use the **Normal** column.
22
+ The **`query` tool** uses retrieval-tuned weights (search 0.70) derived from a held-out judged-relevance eval (v0.13.0) — more weight on topical relevance measurably improves graded NDCG@10 without demoting the newest-correct version of evolving docs. `search`, `vsearch`, `memory_retrieve`, and the context-surfacing hook **keep the Normal column by default**. Since v0.21.0 a config knob — `retrieval.mcp_direct_tuned_weights: true` (or env `CLAWMEM_MCP_DIRECT_TUNED_WEIGHTS=true`) switches the three MCP direct tools' non-recency scoring to the `query`-tool column; it ships **off** because the eval evidence covered only the hybrid `query` pipeline, and the flip is gated on a direct-pipeline eval (see [configuration](../reference/configuration.md)).
23
+
24
+ Threshold note: EOS-anchored last-token embedding models (e.g. zembed-1 served correctly) produce a **compressed-high similarity band** — unrelated pairs sit near ~0.4 rather than ~0.2. Relative ordering is what matters; treat absolute `minScore` cutoffs as model-dependent rather than universal.
23
25
 
24
26
  ## Signal breakdown
25
27
 
@@ -60,9 +60,18 @@ ZeroEntropy's distillation-paired stack — best retrieval quality, total ~16 GB
60
60
  | Reranker | 8090 | [zerank-2 seq-cls sidecar](../../extras/rerankers/zerank-2-seq/) (transformers, bf16) | ~9 GB | SOTA reranker — **not** a GGUF |
61
61
 
62
62
  ```bash
63
- # Embedding (zembed-1) — -ub MUST equal -b for non-causal attention
63
+ # Embedding (zembed-1) — -ub MUST equal -b for non-causal attention.
64
+ # --pooling last: zembed-1 is a last-token model; declare it explicitly.
65
+ # --override-kv ...add_eos_token: last-token models read the embedding at their EOS
66
+ # anchor. If the GGUF conversion lost add_eos_token, the server never appends the
67
+ # terminator and similarity collapses to last-token identity (see troubleshooting →
68
+ # "Vector search returns weak or irrelevant results"). The override restores it and
69
+ # is a no-op when the metadata is already correct. Do NOT add these two flags to
70
+ # mean-pooling models like the default EmbeddingGemma.
64
71
  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
72
+ --embeddings --pooling last \
73
+ --override-kv tokenizer.ggml.add_eos_token=bool:true \
74
+ --port 8088 --host 0.0.0.0 -ngl 99 -c 8192 -b 2048 -ub 2048
66
75
 
67
76
  # Reranker (zerank-2) — seq-cls SIDECAR (transformers, bf16), NOT a llama-server GGUF:
68
77
  cd extras/rerankers/zerank-2-seq
@@ -1,6 +1,6 @@
1
1
  # Upgrading ClawMem
2
2
 
3
- Guide for upgrading between released versions. Current: **v0.10.4**.
3
+ Guide for upgrading between released versions. Current: **v0.21.0**.
4
4
 
5
5
  ClawMem upgrades are designed to be drop-in: pull the new version, restart any long-lived processes, and the SQLite schema auto-migrates on first open. This guide documents per-version specifics for upgrades that have additional considerations beyond the quick path below.
6
6
 
@@ -59,6 +59,24 @@ docker compose up -d reranker # /v1/rerank on :8090
59
59
 
60
60
  ---
61
61
 
62
+ ## v0.21.0: MCP internal-collection exclusion + embedding-geometry canary
63
+
64
+ No migration command required. Two things to know:
65
+
66
+ **Behavior change — `_clawmem` excluded from MCP retrieval by default.** `search`, `vsearch`, `query`, `query_plan`, `memory_retrieve`, and `find_similar` no longer return the system-internal `_clawmem` collection (observations/deductions/handoffs) unless asked. If a workflow depended on those appearing in MCP results, pass `includeInternal: true` or name `_clawmem` in an explicit `collection` filter. `intent_search`, `find_causal_links`, `kg_query`, `session_log`, and `timeline` are unfiltered by design, and hooks already filtered internal docs — `<vault-context>` behavior is unchanged. Details: [docs/reference/mcp-tools.md](../reference/mcp-tools.md).
67
+
68
+ **What auto-applies on first open** (additive, idempotent — same contract as prior releases): the `embed_canary` and `vault_flags` tables, and an `embed_input_fp` column on `content_vectors`.
69
+
70
+ **No re-embed required.** Existing vectors stay valid. Pre-0.21.0 rows carry no input fingerprint, so `clawmem doctor`'s new sampled vector validation checks them structurally and flags title provenance as unavailable ("legacy") until each document's next natural re-embed — informational, not an error.
71
+
72
+ **The canary baseline seeds itself.** The first `clawmem embed` run after upgrade (including a timer-fired one) probes the embedding server with a pair-separation battery and persists a first-healthy baseline; subsequent runs alert relative to it, and a broken-geometry server now aborts `embed --force` BEFORE anything is cleared. `--force-geometry` (proceed despite a failed probe; the vault is flagged tainted until a verified rebuild) and `--force --recalibrate-canary` (replace the baseline after a deliberate model/server change) are operator overrides, not upgrade steps.
73
+
74
+ **Opt-in knob:** `retrieval.mcp_direct_tuned_weights` (config) / `CLAWMEM_MCP_DIRECT_TUNED_WEIGHTS` (env), default `false` — scores the MCP direct tools' non-recency queries with the retrieval-tuned `query`-tool weights. Leave it off unless you have direct-pipeline eval evidence for your vault.
75
+
76
+ Upgrades from v0.13 → v0.20 shipped no steps beyond the [quick path](#quick-path) — schema changes auto-apply; see [RELEASE_NOTES.md](../../RELEASE_NOTES.md) for what each version changed.
77
+
78
+ ---
79
+
62
80
  ## v0.12.0: query reranking blend (no action required)
63
81
 
64
82
  v0.12.0 changes the `query` tool's rerank/RRF blend so the cross-encoder reranker can promote the best document to the top — the previous blend left RRF #1 mathematically immovable. It is a pure ranking-quality change: **no migration, no schema change, no config change.** It applies automatically on upgrade. Hooks and the per-session MCP stdio server pick it up on their next invocation; restart any long-lived `query` host — `clawmem serve` and persistent MCP/daemon processes — to pick up the improved ordering. The default reranker (`qwen3-reranker-0.6B`) is unchanged, and the blend improvement applies whatever reranker `:8090` serves. (`intent_search` and the context-surfacing hook keep their existing blends.)
@@ -145,7 +163,7 @@ Hooks and the stdio MCP server pick up the new binary automatically on their nex
145
163
  - **`plugins.slots.memory: "clawmem"` replaces `plugins.slots.contextEngine: "clawmem"`.** On the new pure-memory plugin, the exclusive slot is `memory`. The `setup openclaw` next-steps output tells you to run `openclaw plugins enable clawmem`, which sets the slot and disables competing memory plugins (`memory-core`, `memory-lancedb`) in a single command. You do NOT need to run the older `openclaw config set plugins.slots.contextEngine clawmem` pattern on v0.10.0.
146
164
  - **`src/openclaw/package.json` is now the plugin's discovery manifest.** OpenClaw v2026.4.11's `discoverInDirectory` reads `package.json` for the `openclaw.extensions` field and uses that to decide whether a directory under `~/.openclaw/extensions/` is a valid plugin. The older `openclaw.plugin.json` manifest is still shipped and parsed at runtime, but it is not sufficient to pass discovery on v2026.4.11+ without the `package.json` companion file. v0.10.0 adds the `package.json` to the plugin source tree, and `clawmem setup openclaw` verifies it is present before copying.
147
165
  - **`clawmem setup openclaw` defaults to recursive copy instead of symlink.** OpenClaw v2026.4.11 walks `~/.openclaw/extensions/` with `readdirSync({ withFileTypes: true })` and uses `dirent.isDirectory()` to descend into candidate plugin directories. Symlinks to directories report `isDirectory() === false` on that API shape, so a symlinked plugin is silently skipped during discovery. v0.10.0's `cmdSetupOpenClaw` therefore copies the plugin source into `~/.openclaw/extensions/clawmem/` with `cpSync(..., { recursive: true, dereference: true })`. A `--link` opt-in flag preserves the old symlink behavior for local development and for older OpenClaw versions, with a warning that v2026.4.11+ discovery will skip the symlink. Setup is idempotent: any existing plugin directory or stale symlink is removed before the new copy is written.
148
- - **Multi-user ownership check (OpenClaw v2026.4.11+).** If the gateway runs as a dedicated system user (e.g. `openclaw`) and you run `clawmem setup openclaw` as a different user (e.g. `sciros`), the copied plugin directory is owned by the installer user, but OpenClaw's ownership check rejects it with `suspicious ownership (uid=1001, expected uid=997 or root)`. This is a security feature that prevents a privileged gateway process from loading code a less-privileged user dropped into its extensions directory. Fix: `sudo chown -R <gateway-user>:<gateway-group> ~/.openclaw/extensions/clawmem`. Single-user installs where you ARE the gateway user are not affected — your own user owns the copy, and the ownership check passes.
166
+ - **Multi-user ownership check (OpenClaw v2026.4.11+).** If the gateway runs as a dedicated system user (e.g. `openclaw`) and you run `clawmem setup openclaw` as a different user (e.g. `alice`), the copied plugin directory is owned by the installer user, but OpenClaw's ownership check rejects it with `suspicious ownership (uid=1001, expected uid=997 or root)`. This is a security feature that prevents a privileged gateway process from loading code a less-privileged user dropped into its extensions directory. Fix: `sudo chown -R <gateway-user>:<gateway-group> ~/.openclaw/extensions/clawmem`. Single-user installs where you ARE the gateway user are not affected — your own user owns the copy, and the ownership check passes.
149
167
 
150
168
  ### Rollback
151
169
 
@@ -191,7 +209,7 @@ Hooks + MCP stdio pick up new code automatically on next invocation — no resta
191
209
 
192
210
  When the user's prompt mentions entities already known to the vault (via `entity_nodes`), `context-surfacing` now appends a token-bounded `<vault-facts>` block of raw SPO triple lines to `<vault-context>`, alongside the existing `<facts>` / `<relationships>` blocks. This feeds the model current-state knowledge about entities the user is talking about, without requiring the agent to call `kg_query` explicitly.
193
211
 
194
- - **Three-path entity seeding** — canonical-ID regex (e.g. `default:project:clawmem`) → proper-noun extraction via `resolveEntityTypeExact` → longer-first n-gram scan (3-gram > 2-gram > 1-gram) for lowercase/hyphenated vocabulary like `forge-stack`, `oauth2`, `vm 202`. All three paths run prompt-only — entity seeds NEVER come from surfaced doc bodies, so topic-boosted off-topic docs cannot pollute the facts block.
212
+ - **Three-path entity seeding** — canonical-ID regex (e.g. `default:project:clawmem`) → proper-noun extraction via `resolveEntityTypeExact` → longer-first n-gram scan (3-gram > 2-gram > 1-gram) for lowercase/hyphenated vocabulary like `side-project`, `oauth2`, `vm 12`. All three paths run prompt-only — entity seeds NEVER come from surfaced doc bodies, so topic-boosted off-topic docs cannot pollute the facts block.
195
213
  - **Profile-gated token sub-budget** — `factsTokens=0` on `speed` disables the stage entirely. `balanced` uses 200 tokens, `deep` uses 250. The sub-budget is dedicated — `<vault-facts>` cannot steal budget from `<facts>` or `<relationships>`.
196
214
  - **Truncation** — at the triple boundary, never mid-triple, never emits an empty block.
197
215
  - **Fail-open** — empty entity set → skip. Budget too small → drop block. Per-entity DB error → skip that entity. Any exception in the stage → return baseline `vault-context` unchanged.
@@ -70,7 +70,7 @@ llama-server -m Qwen3-Reranker-0.6B-Q8_0.gguf \
70
70
  --reranking --port 8090 --host 0.0.0.0 -ngl 99 -c 2048 --batch-size 512
71
71
  ```
72
72
 
73
- > **SOTA upgrade (16GB+ GPU):** Replace embedding with zembed-1-Q4_K_M (2560d, `-b 2048 -ub 2048`). For the reranker, use the **zerank-2 seq-cls sidecar** (transformers, bf16) at [`extras/rerankers/zerank-2-seq/`](../extras/rerankers/zerank-2-seq/) — the `zerank-2-Q4_K_M` GGUF is deprecated (llama.cpp drops its score head). See [the inference services guide](guides/inference-services.md). **CC-BY-NC-4.0** — non-commercial only.
73
+ > **SOTA upgrade (16GB+ GPU):** Replace embedding with zembed-1-Q4_K_M (2560d, `-b 2048 -ub 2048`, **plus `--pooling last --override-kv tokenizer.ggml.add_eos_token=bool:true`** — last-token models need their EOS anchor; the exact launch line is in [the inference services guide](guides/inference-services.md)). For the reranker, use the **zerank-2 seq-cls sidecar** (transformers, bf16) at [`extras/rerankers/zerank-2-seq/`](../extras/rerankers/zerank-2-seq/) — the `zerank-2-Q4_K_M` GGUF is deprecated (llama.cpp drops its score head). **CC-BY-NC-4.0** — non-commercial only.
74
74
 
75
75
  See [GPU services guide](guides/systemd-services.md) for systemd setup and remote GPU configuration.
76
76
 
@@ -7,7 +7,7 @@ Complete command reference for the ClawMem memory engine. Always use the `bin/cl
7
7
  ```bash
8
8
  clawmem init # Initialize vault (creates SQLite DB)
9
9
  clawmem status # Quick index status
10
- clawmem doctor # Full health check (GPU connectivity, index integrity)
10
+ clawmem doctor # Full health check (GPU connectivity, index integrity, embedding-geometry canary, sampled vector validation)
11
11
  ```
12
12
 
13
13
  ## Collection management
@@ -30,8 +30,10 @@ clawmem mine <dir> --dry-run # Preview without importing
30
30
  clawmem mine <dir> --synthesize # v0.7.2: import + post-import LLM fact extraction
31
31
  clawmem mine <dir> --synthesize --synthesis-max-docs 50 # Cap synthesis to first 50 conversations (default 20)
32
32
  clawmem reindex # Force re-scan all collections
33
- clawmem embed # Embed all un-embedded fragments
34
- clawmem embed --force # Re-embed everything (clears existing vectors)
33
+ clawmem embed # Embed all un-embedded fragments (geometry-canary preflight runs first)
34
+ clawmem embed --force # Re-embed everything (clears existing vectors; aborts BEFORE clearing if the canary preflight fails)
35
+ clawmem embed --force --force-geometry # v0.21.0: proceed despite a failed/unavailable canary — vault is tainted until a verified rebuild
36
+ clawmem embed --force --recalibrate-canary # v0.21.0: replace the stored canary baseline after a deliberate model/server change (requires --force)
35
37
  ```
36
38
 
37
39
  ### Conversation Import
@@ -38,6 +38,7 @@ Full provider matrix and behavior: [../guides/cloud-embedding.md](../guides/clou
38
38
  |---|---|---|
39
39
  | `CLAWMEM_PROFILE` | `balanced` | `speed` / `balanced` / `deep`. Sets the kept-score ratio (65% / 55% / 45%), vector timeout, max results, and the `factsTokens` sub-budget. Only `deep` adds query expansion + reranking to the hook path. `speed` makes hooks BM25-only (sub-500ms). |
40
40
  | `CLAWMEM_NUDGE_INTERVAL` | `15` | Prompts between lifecycle tool use before a `<vault-nudge>` is injected. `0` to disable. |
41
+ | `CLAWMEM_MCP_DIRECT_TUNED_WEIGHTS` | `false` | When `true`, the MCP direct tools (`search`, `vsearch`, `memory_retrieve`) score non-recency queries with the retrieval-tuned `query`-tool weights (search 0.70) instead of the Normal weights. Ships **off**: the eval evidence covered only the hybrid `query` pipeline; flipping the default is gated on a direct-pipeline eval. YAML equivalent in `config.yaml`: `retrieval.mcp_direct_tuned_weights: true` (env wins). |
41
42
 
42
43
  The context-surfacing hook `timeout` is **not** an env var — it lives in `~/.claude/settings.json` (8s default). See [../troubleshooting.md](../troubleshooting.md) → *Tuning the context-surfacing hook timeout*.
43
44
 
@@ -4,6 +4,10 @@ Complete reference for ClawMem's MCP server tools. These let AI agents search, r
4
4
 
5
5
  ## Retrieval
6
6
 
7
+ **Internal-collection visibility (v0.21.0):** the retrieval tools `search`, `vsearch`, `query`, `query_plan`, `memory_retrieve`, and `find_similar` exclude the system-internal `_clawmem` collection (observations/deductions/handoffs) by default. Opt-ins: pass `includeInternal: true` (all six tools), or — on the tools that expose a `collection` parameter (`search`, `vsearch`, `query`) — name `_clawmem` explicitly in the filter. `find_similar` auto-includes internal results when the REFERENCE document is itself internal. `intent_search`, `find_causal_links`, `kg_query`, `session_log`, and `timeline` are NOT filtered — system memory is their substrate by design.
8
+
9
+ **Degraded vector results (v0.21.0):** under default exclusion the vector scan escalates its depth to fill `limit` with allowed documents, up to a hard cap. When the cap prevents an exhaustive scan and the result is under-filled, `structuredContent` carries `degraded: true` with `degradedReason`: `"excluded-dominant"` (distinct excluded docs account for the shortfall — the guidance line suggests `includeInternal: true` or a refined query) or `"cap-truncation"` (shortfall driven by fragment dedup, neutral guidance). Multi-leg routes (`query`, `query_plan`, `memory_retrieve` complex mode) aggregate `degraded = any(leg)` and list per-leg reasons in `structuredContent.degradedLegs`; single-vector routes (`vsearch`, `find_similar`, `memory_retrieve`'s other modes) report the flat `degraded` + `degradedReason` pair. A small vault whose whole index is scanned without hitting the cap returns a plain short list with NO marker.
10
+
7
11
  ### memory_retrieve
8
12
 
9
13
  **Recommended entry point.** Auto-classifies query and routes to the optimal backend.
@@ -14,6 +18,7 @@ Complete reference for ClawMem's MCP server tools. These let AI agents search, r
14
18
  | `mode` | enum | `auto` | Override: `keyword`, `semantic`, `causal`, `timeline`, `discovery`, `complex`, `hybrid` |
15
19
  | `limit` | number | 10 | Max results |
16
20
  | `compact` | boolean | true | Compact output (snippets vs full content) |
21
+ | `includeInternal` | boolean | false | Include system-internal `_clawmem` docs |
17
22
  | `vault` | string | — | Named vault |
18
23
 
19
24
  Auto-routing:
@@ -35,6 +40,7 @@ Full hybrid pipeline: BM25 + vector + query expansion + cross-encoder reranking.
35
40
  | `collection` | string | — | Filter by collection (comma-separated for multi) |
36
41
  | `intent` | string | — | Domain hint for ambiguous queries (steers expansion, reranking, chunk selection) |
37
42
  | `candidateLimit` | number | 30 | Candidates for reranking (tune precision vs speed) |
43
+ | `includeInternal` | boolean | false | Include system-internal `_clawmem` docs |
38
44
  | `vault` | string | — | Named vault |
39
45
 
40
46
  BM25 strong-signal bypass: skips expansion when top BM25 hit >= 0.85 with gap >= 0.15 (disabled when `intent` is provided).
@@ -49,6 +55,7 @@ BM25 only. Zero GPU cost.
49
55
  | `limit` | number | 10 | Max results |
50
56
  | `compact` | boolean | true | Compact output |
51
57
  | `collection` | string | — | Filter by collection |
58
+ | `includeInternal` | boolean | false | Include system-internal `_clawmem` docs |
52
59
  | `vault` | string | — | Named vault |
53
60
 
54
61
  ### vsearch
@@ -61,6 +68,7 @@ Vector only. Semantic similarity.
61
68
  | `limit` | number | 10 | Max results |
62
69
  | `compact` | boolean | true | Compact output |
63
70
  | `collection` | string | — | Filter by collection |
71
+ | `includeInternal` | boolean | false | Include system-internal `_clawmem` docs |
64
72
  | `vault` | string | — | Named vault |
65
73
 
66
74
  ### intent_search
@@ -84,6 +92,7 @@ Multi-topic decomposition. Use for complex queries spanning multiple subjects.
84
92
  | `query` | string | required | Complex or multi-topic query |
85
93
  | `limit` | number | 10 | Max results |
86
94
  | `compact` | boolean | true | Compact output |
95
+ | `includeInternal` | boolean | false | Include system-internal `_clawmem` docs |
87
96
  | `vault` | string | — | Named vault |
88
97
 
89
98
  ## Document access
@@ -120,6 +129,7 @@ k-NN vector neighbors of a reference document.
120
129
  |-------|------|---------|-------------|
121
130
  | `file` | string | required | Path of reference document |
122
131
  | `limit` | number | 5 | Max results |
132
+ | `includeInternal` | boolean | false | Include system-internal `_clawmem` docs (auto-included when the reference doc is itself internal) |
123
133
  | `vault` | string | — | Named vault |
124
134
 
125
135
  ### find_causal_links
@@ -93,10 +93,13 @@ Common issues when running ClawMem with hooks, MCP server, or OpenClaw plugin. O
93
93
  - Fix: re-deploy a working reranker — the **zerank-2 seq-cls sidecar** (`extras/rerankers/zerank-2-seq/`, see CLAUDE.md "SOTA upgrade") or the default `qwen3-reranker-0.6B` — and confirm `CLAWMEM_RERANK_URL` points at it. Re-run `clawmem rerank-health`; it should exit 0. For remote-sidecar setups, schedule `clawmem-rerank-health.timer` (CLAUDE.md → Background Services) so a future silent reversion pages you.
94
94
 
95
95
  **Vector search returns weak or irrelevant results even though embeddings exist**
96
- - BM25/keyword search works and `doctor` shows vectors present + consistent, but `vsearch`/`find_similar` (and the vector half of `query`) return loosely-related results, or "the same few docs regardless of query." This is an embedding-**quality** problem, not a ClawMem index problem: the model is producing poorly-discriminating vectors. The most common cause is a **server-side pooling/normalization misconfiguration** — e.g. serving a last-token model (Qwen3-Embedding family) without `--pooling last`, or without L2 normalization. Mean-pooling a last-token model gives usable self-retrieval but collapsed semantic separation (paraphrases score ~0.5 instead of ~0.85).
97
- - Diagnose: embed two paraphrases and one unrelated sentence through your endpoint and compare cosine similarity. A healthy model scores the paraphrase pair > 0.75 and the unrelated pair < 0.45. If paraphrases score ~0.5 with weak separation, the model/serving is the problem, not ClawMem.
98
- - Fix: launch the embedding `llama-server` with the pooling its model requires (`--pooling last` for Qwen3-Embedding / last-token models) and ensure outputs are L2-normalized. Re-run the diagnostic; once separation is healthy, retrieval recovers. A re-embed is not required if the dimension is unchanged, though `clawmem embed --force` after the fix guarantees a clean rebuild.
99
- - Aside: auto-generated `_clawmem/` system docs (observations/deductions) sit near the embedding centroid and can dominate pure-vector tools when discrimination is weakfixing the model is the real remedy, not filtering.
96
+ - BM25/keyword search works and `doctor` shows vectors present + consistent, but `vsearch`/`find_similar` (and the vector half of `query`) return loosely-related results, or "the same few docs regardless of query." This is an embedding-**quality** problem, not a ClawMem index problem: the model is producing poorly-discriminating vectors. Two common causes, both server-side:
97
+ - **Pooling misconfiguration** serving a last-token model (Qwen3-Embedding family) without `--pooling last`, or without L2 normalization. Mean-pooling a last-token model gives usable self-retrieval but collapsed semantic separation (paraphrases score ~0.5 instead of ~0.85).
98
+ - **Missing EOS anchor** a last-token model whose GGUF conversion lost `tokenizer.ggml.add_eos_token`: the server never appends the terminator the model reads its embedding from, so last-token pooling reads an arbitrary final text token. Signature: similarity tracks how texts END, not what they mean — identical-vocabulary pairs score low; similarity swings with the truncation point; texts sharing a final word score deceptively high; self-similarity stays ~1.0 (so stored-vs-fresh checks pass). Verified real-world: this exact failure served an entire vault ~0.33 on echo pairs while basic-English probes looked healthy.
99
+ - Diagnose: embed two paraphrases and one unrelated sentence through your endpoint and compare cosine similarity. A healthy model scores the paraphrase pair > 0.75 and the unrelated pair < 0.45. Also test a near-identical pair differing only in the final word it must score HIGH; low means an unanchored last-token readout. Beware the shared-suffix confound when testing terminators by hand: appending ANY common suffix to both texts inflates similarity by last-token identity — only genuine semantic separation (related high AND unrelated low) proves the fix. `clawmem doctor` runs this battery automatically (geometry canary), and `clawmem embed` refuses to build against a failing geometry (override: `--force-geometry`).
100
+ - Fix: launch the embedding `llama-server` with the pooling its model requires (`--pooling last` for Qwen3-Embedding / last-token models), restore the EOS append when the GGUF lost it (`--override-kv tokenizer.ggml.add_eos_token=bool:true` — a no-op when the metadata is already correct), and ensure outputs are L2-normalized. **A full `clawmem embed --force` is REQUIRED after any serving-side pooling/normalization/EOS change**: the old vectors are faithful to the old geometry, and two geometries at the same dimension are mutually incompatible — fresh queries against stale vectors are cosine-meaningless.
101
+ - Note on `_clawmem/` system docs (observations/deductions): since v0.21.0 the MCP retrieval tools exclude the `_clawmem` collection by default (pass `includeInternal: true` to include it; an explicit `collection` filter naming `_clawmem` also overrides). If internal docs dominate results even with a HEALTHY model, that is the composite-scoring floor (system docs carry high confidence and non-decaying recency), not geometry — the default exclusion is the remedy there.
102
+ - Operational: don't run `embed --force` while the file watcher is indexing bulk changes — stop the watcher or expect write contention (the run now survives transient `SQLITE_BUSY`, but contention still slows it). If you pipe embed output through `tee`, remember the pipeline exit code is `tee`'s — check `PIPESTATUS[0]` for the embed's own status.
100
103
 
101
104
  **kg_query returns empty for every entity**
102
105
  - `entity_triples` is populated by the decision-extractor Stop hook from observer-emitted `<triples>` blocks. Zero rows typically means either (a) the Stop hook has never fired in this vault, or (b) the observer LLM is not emitting `<triples>` blocks.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clawmem",
3
- "version": "0.20.2",
3
+ "version": "0.21.0",
4
4
  "description": "On-device memory layer for AI agents. Claude Code, OpenClaw, and Hermes. Hooks + MCP server + hybrid RAG search.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,34 @@
1
+ /**
2
+ * SQLITE_BUSY detection + bounded ASYNC retry (VSEARCH-TRUST-HARDENING (f).2).
3
+ *
4
+ * The awaits between attempts let the event loop breathe so the embed-lease heartbeat
5
+ * (a setInterval on the same loop) fires; the caller's leaseLost flag is re-checked
6
+ * between attempts so a reclaimed lease ABORTS instead of writing as a zombie holder.
7
+ * Retry scope is SQLITE_BUSY only — every other error (including FatalVectorError
8
+ * dimension/model/schema aborts) propagates unchanged on the first throw.
9
+ */
10
+ import { EmbedLeaseLostError } from "./store.ts";
11
+
12
+ const SQLITE_BUSY_RE = /database is locked|SQLITE_BUSY/i;
13
+
14
+ export function isSqliteBusyError(err: unknown): boolean {
15
+ return err instanceof Error && SQLITE_BUSY_RE.test(err.message);
16
+ }
17
+
18
+ export async function retryOnBusyAsync<T>(
19
+ fn: () => T,
20
+ label: string,
21
+ isLeaseLost: () => boolean,
22
+ opts?: { delaysMs?: number[]; onRetry?: (label: string, attempt: number, delayMs: number) => void }
23
+ ): Promise<T> {
24
+ const delays = opts?.delaysMs ?? [1000, 2000, 4000];
25
+ for (let attempt = 0; ; attempt++) {
26
+ try { return fn(); }
27
+ catch (err) {
28
+ if (!isSqliteBusyError(err) || attempt >= delays.length) throw err;
29
+ opts?.onRetry?.(label, attempt + 1, delays[attempt]!);
30
+ await new Promise(res => setTimeout(res, delays[attempt]!));
31
+ if (isLeaseLost()) throw new EmbedLeaseLostError();
32
+ }
33
+ }
34
+ }