clawmem 0.14.0 → 0.15.1
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 +165 -716
- package/CLAUDE.md +4 -786
- package/README.md +20 -191
- package/SKILL.md +157 -711
- package/docs/clawmem-architecture.excalidraw +2415 -0
- package/docs/clawmem-architecture.png +0 -0
- package/docs/clawmem_hero.jpg +0 -0
- package/docs/concepts/architecture.md +413 -0
- package/docs/concepts/composite-scoring.md +133 -0
- package/docs/concepts/hooks-vs-mcp.md +156 -0
- package/docs/concepts/multi-vault.md +71 -0
- package/docs/contributing.md +101 -0
- package/docs/guides/cloud-embedding.md +134 -0
- package/docs/guides/hermes-plugin.md +187 -0
- package/docs/guides/inference-services.md +144 -0
- package/docs/guides/multi-vault-config.md +84 -0
- package/docs/guides/openclaw-plugin.md +306 -0
- package/docs/guides/setup-hooks.md +146 -0
- package/docs/guides/setup-mcp.md +76 -0
- package/docs/guides/systemd-services.md +332 -0
- package/docs/guides/upgrading.md +566 -0
- package/docs/internals/entity-resolution.md +135 -0
- package/docs/internals/graph-traversal.md +85 -0
- package/docs/internals/intent-search-pipeline.md +103 -0
- package/docs/internals/query-pipeline.md +100 -0
- package/docs/introduction.md +104 -0
- package/docs/quickstart.md +158 -0
- package/docs/reference/cli.md +195 -0
- package/docs/reference/configuration.md +101 -0
- package/docs/reference/mcp-tools.md +336 -0
- package/docs/reference/rest-api.md +204 -0
- package/docs/troubleshooting.md +339 -0
- package/package.json +2 -1
- package/src/memory.ts +2 -0
- package/src/store.ts +74 -9
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# Claude Code hooks vs MCP tools
|
|
2
|
+
|
|
3
|
+
ClawMem delivers memory through two tiers: hooks handle about 90% of context delivery automatically, while MCP server tools (available to any MCP-compatible client, not just Claude Code) cover the remaining 10% when the agent needs to escalate.
|
|
4
|
+
|
|
5
|
+
## Tier 2 — Hooks (automatic)
|
|
6
|
+
|
|
7
|
+
Hooks fire on Claude Code lifecycle events with zero agent effort:
|
|
8
|
+
|
|
9
|
+
| Hook | Trigger | Budget | What it does |
|
|
10
|
+
|------|---------|--------|-------------|
|
|
11
|
+
| `context-surfacing` | UserPromptSubmit | profile-driven (default 800 tokens + factsTokens sub-budget) | Searches vault for context relevant to the user's prompt. Injects results as `<vault-context>` XML with four inner blocks: `<instruction>` (framing — always present), `<facts>` (the surfaced docs), `<relationships>` (memory-graph edges between surfaced docs, v0.7.1), and `<vault-facts>` (raw SPO triples for prompt-seeded entities, v0.9.0 §11.1). Also applies a session-scoped topic boost when a focus file is set for the session (v0.9.0 §11.4). |
|
|
12
|
+
| `postcompact-inject` | SessionStart (after compact) | 1200 tokens | Re-injects authoritative state after context window compaction. |
|
|
13
|
+
| `curator-nudge` | SessionStart | 200 tokens | Surfaces maintenance suggestions from the curator report. |
|
|
14
|
+
| `precompact-extract` | PreCompact | — | Extracts decisions, file paths, and open questions before compaction. Writes `precompact-state.md`. |
|
|
15
|
+
| `decision-extractor` | Stop | — | LLM extracts observations from the conversation. Infers causal links. Detects contradictions with prior decisions. Extracts SPO triples from decision/preference/milestone/problem facts. |
|
|
16
|
+
| `handoff-generator` | Stop | — | LLM summarizes the session for cross-session continuity. |
|
|
17
|
+
| `feedback-loop` | Stop | — | Tracks which notes were referenced. Boosts their confidence. Per-turn recall attribution marks which surfaced docs were actually cited. |
|
|
18
|
+
|
|
19
|
+
### How context-surfacing works
|
|
20
|
+
|
|
21
|
+
1. Validate prompt (skip slash commands, greetings, heartbeats, duplicates)
|
|
22
|
+
2. Load performance profile (`speed` / `balanced` / `deep`) for budget and thresholds
|
|
23
|
+
3. **Resolve session focus topic** (v0.9.0 §11.4) — read `~/.cache/clawmem/sessions/<id>.focus` if present. If set, thread as `intent` hint to the next stages
|
|
24
|
+
4. Search: vector (if profile enables it, with profile-driven timeout) + BM25 supplement
|
|
25
|
+
5. Filter: exclude private paths, snoozed documents, noise
|
|
26
|
+
6. Score: composite scoring (relevance + recency + confidence + quality)
|
|
27
|
+
7. **Apply session focus topic boost** (v0.9.0 §11.4) — if a focus topic was resolved, multiply matching docs' compositeScore by 1.4× and non-matching by 0.75× (floor 50%). NO-OP when zero docs match — the baseline ordering passes through unchanged, so the threshold filter never shrinks the result set because of a non-matching topic
|
|
28
|
+
8. Adaptive threshold + memory-type diversification
|
|
29
|
+
9. Build facts block within `tokenBudget - instructionCost` so the always-on `<instruction>` frame fits
|
|
30
|
+
10. Fetch relationship snippets from `memory_relations` for edges where BOTH endpoints are in the surfaced doc set — these become a `<relationships>` block, the first thing dropped when the payload would overflow budget
|
|
31
|
+
11. **Seed entities from the prompt** (v0.9.0 §11.1) — three-path extraction (canonical-id regex → proper-noun validation via `resolveEntityTypeExact` → longer-first n-gram scan). Prompt-only: seeds NEVER come from surfaced doc bodies, so topic-boosted off-topic docs cannot pollute the facts block
|
|
32
|
+
12. **Query SPO triples for seeded entities**, dedupe by `(subject, predicate, object)` across all entities, emit a token-bounded `<vault-facts>` block using the dedicated `factsTokens` sub-budget (speed=0 disables the stage, balanced=200, deep=250). Truncate at triple boundary, never mid-triple, never emit an empty block. Fail-open on every error path
|
|
33
|
+
13. Inject as `<vault-context><instruction>...</instruction><facts>...</facts><relationships>...</relationships><vault-facts>...</vault-facts></vault-context>` XML in the prompt
|
|
34
|
+
|
|
35
|
+
The `<instruction>` frame tells the model to treat the surfaced facts as background knowledge it already holds unless the user corrects them, reducing prompt-level ambiguity about how to use the injected context. The `<relationships>` block exposes the vault's knowledge graph (semantic, supporting, contradicts, causal, temporal edges) directly in-prompt so the model can reason over document connections without having to call `intent_search`. The `<vault-facts>` block adds raw SPO triples for prompt-seeded entities so the model has structured "what is currently true about these entities" without needing an explicit `kg_query`. `<vault-facts>` / `<relationships>` landed in v0.9.0 / v0.7.1 respectively.
|
|
36
|
+
|
|
37
|
+
### Tuning context-surfacing with profiles
|
|
38
|
+
|
|
39
|
+
Set `CLAWMEM_PROFILE` to adjust the context-surfacing hook's behavior:
|
|
40
|
+
|
|
41
|
+
| Profile | Token budget | `factsTokens` | Max results | Vector | Vector timeout | Score ratio | Activation floor | Deep escalation |
|
|
42
|
+
|---------|-------------|---------------|-------------|--------|----------------|-------------|-----------------|-----------------|
|
|
43
|
+
| `speed` | 400 | 0 (disabled) | 5 | Off | — | 65% | 0.24 | No |
|
|
44
|
+
| `balanced` (default) | 800 | 200 | 10 | On | 900ms | 55% | 0.20 | No |
|
|
45
|
+
| `deep` | 1200 | 250 | 15 | On | 2000ms | 45% | 0.16 | Yes |
|
|
46
|
+
|
|
47
|
+
`factsTokens` is a dedicated sub-budget for the `<vault-facts>` KG injection block (v0.9.0 §11.1) that cannot steal from the main `tokenBudget`. Setting `factsTokens: 0` on a profile disables the stage entirely.
|
|
48
|
+
|
|
49
|
+
Profiles only affect the automatic context-surfacing hook. MCP tools are not affected — agents control their own `limit`, `compact`, and tool selection per call.
|
|
50
|
+
|
|
51
|
+
### Adaptive thresholds
|
|
52
|
+
|
|
53
|
+
Context-surfacing uses adaptive ratio-based thresholds that adjust to your vault's score distribution instead of fixed absolute values. The filter works in two steps:
|
|
54
|
+
|
|
55
|
+
1. **Activation floor** — if the best result in the entire set scores below the activation floor (e.g., 0.20 for balanced), the hook returns empty. This prevents surfacing all-weak results where even the top hit isn't relevant enough to be useful.
|
|
56
|
+
|
|
57
|
+
2. **Score ratio** — results are kept if they score within the ratio of the best result's composite score. For balanced at 55%, a best score of 0.60 keeps everything above 0.33 (`0.60 * 0.55`). An absolute floor (0.15 for balanced) prevents the ratio from going too low when the best score is itself marginal.
|
|
58
|
+
|
|
59
|
+
This adapts to vault size (smaller vaults produce higher absolute scores), embedding model (different cosine similarity distributions), document quality (the 0.7x-1.3x quality multiplier shifts all scores), and content age (recency decay affects absolute scores but the ratio stays stable).
|
|
60
|
+
|
|
61
|
+
For backward compatibility, set `thresholdMode: "absolute"` in the profile to use fixed `minScore` values instead. MCP tools always use absolute thresholds since agents control their own limits directly.
|
|
62
|
+
|
|
63
|
+
### Deep escalation (deep profile only)
|
|
64
|
+
|
|
65
|
+
On the `deep` profile, context-surfacing uses a budget-aware escalation strategy. After the fast path (BM25 + vector search) completes, the hook checks how much of its 8-second timeout remains. If the fast path finished in under 4 seconds, the remaining time is spent on two additional phases:
|
|
66
|
+
|
|
67
|
+
1. **Query expansion** — the LLM generates lexical and semantic variants of the prompt. These expanded queries run through BM25 to discover candidates that the original query terms missed. New candidates are merged into the result set (deduplicated).
|
|
68
|
+
|
|
69
|
+
2. **Cross-encoder reranking** — the top 15 candidates are scored by the reranker with 2000 chars of document context per candidate. Results are blended (60% original score, 40% reranker score) to avoid over-relying on either signal.
|
|
70
|
+
|
|
71
|
+
Both phases have a hard stop at 6 seconds (leaving 2 seconds of headroom within the hook timeout). If GPU services are unavailable or either phase times out, the hook falls back to the fast-path results. On a GPU system, expansion typically takes ~300ms and reranking ~200ms, so both phases fire on nearly every prompt. On CPU-only systems, the fast path alone usually consumes most of the 4-second budget, so escalation skips naturally.
|
|
72
|
+
|
|
73
|
+
The effect: `deep` profile hooks produce results closer to what the `query` MCP tool returns (which always runs expansion + reranking), while `speed` and `balanced` continue using the fast path only.
|
|
74
|
+
|
|
75
|
+
### Session focus topic boost (v0.9.0 §11.4)
|
|
76
|
+
|
|
77
|
+
The session focus topic is a per-session bias that steers context-surfacing toward a declared topic for the duration of a working session, without writing to SQLite or mutating any lifecycle column. Use it when the user asks to focus on one thing ("let's focus on the auth refactor for this session" / "only surface X-related docs right now") — the topic biases retrieval toward matching docs while leaving the underlying vault state untouched. Clearing the focus at the end of the subsession returns surfacing to baseline.
|
|
78
|
+
|
|
79
|
+
Set / show / clear with the CLI:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
clawmem focus set "authentication flow" --session-id abc123
|
|
83
|
+
clawmem focus show --session-id abc123
|
|
84
|
+
clawmem focus clear --session-id abc123
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
The session ID is resolved from `--session-id <id>`, then `CLAUDE_SESSION_ID`, then `CLAWMEM_SESSION_ID` — Claude Code exposes `CLAUDE_SESSION_ID` natively so the env-var path works automatically inside a Claude Code session. The focus file lives at `~/.cache/clawmem/sessions/<session_id>.focus`.
|
|
88
|
+
|
|
89
|
+
When a focus topic is active:
|
|
90
|
+
|
|
91
|
+
- **Intent-threaded retrieval** — the topic is passed as `intent` to `expandQuery`, `rerank`, and `extractSnippet`. Query expansion generates topic-aware variants, rerank prioritizes topic-aligned segments, and snippet extraction prefers sentences containing the topic tokens. Same `intent` lever that already exists on the query-time pipeline.
|
|
92
|
+
- **Post-composite-score topic boost** — after `applyCompositeScoring` and before the adaptive threshold filter, docs that match all topic tokens (bag-of-word against title/path/body[:800], case-insensitive) get a 1.4× multiplier on `compositeScore`; non-matching docs get a 0.75× demote (clamped at a 0.5 floor). Boost fires per-result so it interacts cleanly with the adaptive threshold — matching docs surface higher, non-matching docs drop.
|
|
93
|
+
- **Zero-match NO-OP** — if no docs in the scored result set match the topic, the boost stage is a no-op. The baseline ordering is returned unchanged, so the adaptive threshold sees byte-identical input and the result set never shrinks because of a non-matching focus. This fail-open contract is locked in by hook-level integration tests that assert byte-equality between a non-matching-topic run and a no-topic run.
|
|
94
|
+
- **Session isolation** — the focus file is keyed by `sessionId`, never touches SQLite, and concurrent sessions on the same host cannot cross-contaminate each other's topic biasing. `CLAWMEM_SESSION_FOCUS` env var is a debug-only override that does NOT provide per-session scoping — do not rely on it for multi-session deployments.
|
|
95
|
+
|
|
96
|
+
### Hook blind spots
|
|
97
|
+
|
|
98
|
+
Hooks filter aggressively — they enforce score thresholds, cap token budgets, and exclude system artifacts. If a memory exists but wasn't surfaced in `<vault-context>`, it doesn't mean it's missing from the vault. It means it didn't make the top-k cut for this prompt.
|
|
99
|
+
|
|
100
|
+
## Tier 3 — MCP Tools (agent-initiated)
|
|
101
|
+
|
|
102
|
+
The agent should escalate to MCP tools only when one of three rules fires:
|
|
103
|
+
|
|
104
|
+
1. **Low-specificity** — `<vault-context>` is empty or missing the specific fact needed
|
|
105
|
+
2. **Cross-session** — the task references prior sessions or decisions ("why did we decide X")
|
|
106
|
+
3. **Pre-irreversible** — about to make a destructive or hard-to-reverse change
|
|
107
|
+
|
|
108
|
+
### Preferred entry point
|
|
109
|
+
|
|
110
|
+
Use `memory_retrieve(query)` — it auto-classifies the query and routes to the optimal backend:
|
|
111
|
+
|
|
112
|
+
- "why did we decide X" → intent_search (causal graph traversal)
|
|
113
|
+
- "what happened last session" → session_log
|
|
114
|
+
- "what else relates to X" → find_similar (vector neighbors)
|
|
115
|
+
- complex multi-topic → query_plan (parallel decomposition)
|
|
116
|
+
- general recall → query (full hybrid pipeline)
|
|
117
|
+
|
|
118
|
+
### Direct routing (when you know which tool to use)
|
|
119
|
+
|
|
120
|
+
| Query type | Tool | Why not query()? |
|
|
121
|
+
|-----------|------|-----------------|
|
|
122
|
+
| Why / what caused / decision | `intent_search` | Graph traversal finds causal chains query() can't |
|
|
123
|
+
| Last session / yesterday | `session_log` | Session-specific data not in search index |
|
|
124
|
+
| What else relates to X | `find_similar` | k-NN vector neighbors, not keyword overlap |
|
|
125
|
+
| Entity facts / relationships | `kg_query` | Structured SPO triples, not document search |
|
|
126
|
+
| Complex multi-topic | `query_plan` | Decomposes into typed parallel retrieval |
|
|
127
|
+
| General recall | `query` | Full hybrid: BM25 + vector + expansion + reranking |
|
|
128
|
+
| Keyword spot check | `search` | BM25 only, zero GPU cost |
|
|
129
|
+
| Conceptual / fuzzy | `vsearch` | Vector only, semantic similarity |
|
|
130
|
+
|
|
131
|
+
`diary_write` and `diary_read` are for non-hooked environments only (Hermes, Gemini, plain MCP clients). In Claude Code, hooks capture observations and handoffs automatically.
|
|
132
|
+
|
|
133
|
+
### Anti-patterns
|
|
134
|
+
|
|
135
|
+
- Do NOT call MCP tools every turn — the three rules above are the only gates
|
|
136
|
+
- Do NOT re-search what's already in `<vault-context>`
|
|
137
|
+
- Do NOT use `query()` for "why" questions — use `intent_search` or `memory_retrieve`
|
|
138
|
+
- Do NOT use `query()` for session history — use `session_log`
|
|
139
|
+
- Do NOT use `kg_query()` for causal "why" questions — use `intent_search`. `kg_query` returns structured facts, not reasoning chains
|
|
140
|
+
- Do NOT use `diary_write` in Claude Code — hooks handle this automatically
|
|
141
|
+
|
|
142
|
+
## Why hooks handle 90%
|
|
143
|
+
|
|
144
|
+
The 90/10 split between hooks and MCP tools is a deliberate architectural response to a real limitation: agents are not reliably proactive with memory tools.
|
|
145
|
+
|
|
146
|
+
When an agent has both native tools (Read, Grep) and memory tools (query, intent_search) available, it will consistently default to the simpler native tools or answer from existing context — even when a vault search would produce better results. This isn't a bug in any particular model. MCP tool calls add latency and consume context window. The agent's implicit cost/benefit calculation favors "answer now" over "search first, answer better." The result is that purely agent-initiated memory systems get underused in practice.
|
|
147
|
+
|
|
148
|
+
Hooks bypass this problem entirely. Context-surfacing fires on every prompt regardless of what the agent decides to do. Decision-extractor captures observations after every response. Feedback-loop tracks what was referenced. The agent doesn't get to skip these — they run as part of the Claude Code lifecycle, not as tool calls the agent can choose to make.
|
|
149
|
+
|
|
150
|
+
The remaining 10% — the MCP tools — covers situations where hooks can't help: the agent needs deeper search than what context-surfacing provided, the question spans multiple sessions, or a destructive action needs a vault check first. These genuinely require agent initiative, and the 3-rule escalation gate keeps the scope narrow enough that agents can follow it.
|
|
151
|
+
|
|
152
|
+
### Making agents more proactive
|
|
153
|
+
|
|
154
|
+
For the proactive operations agents should be doing (pinning critical decisions, snoozing noisy context, running deeper searches when surfaced context is relevant but thin), instruction redundancy helps. Place the routing rules and escalation gates in your global CLAUDE.md or AGENTS.md so they load on every conversation. The trigger block in the README's [Agent Instructions](../README.md#agent-instructions) section is designed for this — it gives the agent routing rules always loaded, with SKILL.md as on-demand deep reference.
|
|
155
|
+
|
|
156
|
+
This stubbornness around proactive memory tool use is unlikely to change until model providers include memory management patterns in their training data. Until then, hooks carry the weight, and instruction redundancy is the best mitigation for the rest.
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Multi-vault configuration
|
|
2
|
+
|
|
3
|
+
By default, ClawMem stores all AI agent memory in a single vault at `~/.cache/clawmem/index.sqlite`. Multi-vault is opt-in for users who want separate memory domains.
|
|
4
|
+
|
|
5
|
+
## When to use multiple vaults
|
|
6
|
+
|
|
7
|
+
- **Project isolation** — keep work and personal memory separate
|
|
8
|
+
- **Shared vs private** — a shared team vault alongside a personal vault
|
|
9
|
+
- **Runtime separation** — separate vaults for Claude Code and OpenClaw (though shared single vault is the recommended default)
|
|
10
|
+
|
|
11
|
+
## Configuration
|
|
12
|
+
|
|
13
|
+
### Via config.yaml
|
|
14
|
+
|
|
15
|
+
```yaml
|
|
16
|
+
# ~/.config/clawmem/config.yaml
|
|
17
|
+
vaults:
|
|
18
|
+
work: ~/.cache/clawmem/work.sqlite
|
|
19
|
+
personal: ~/.cache/clawmem/personal.sqlite
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### Via environment variable
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
export CLAWMEM_VAULTS='{"work":"~/.cache/clawmem/work.sqlite","personal":"~/.cache/clawmem/personal.sqlite"}'
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Environment variables override config file values. Vault paths support `~` expansion.
|
|
29
|
+
|
|
30
|
+
## Using vaults with tools
|
|
31
|
+
|
|
32
|
+
All MCP tools accept an optional `vault` parameter. Omit it for the default vault.
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
# Search the work vault
|
|
36
|
+
query("authentication setup", vault="work", compact=true)
|
|
37
|
+
|
|
38
|
+
# Pin a memory in the personal vault
|
|
39
|
+
memory_pin("remember preference X", vault="personal")
|
|
40
|
+
|
|
41
|
+
# Get lifecycle stats for a specific vault
|
|
42
|
+
lifecycle_status(vault="work")
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Tools that support vault: `query`, `search`, `vsearch`, `intent_search`, `memory_retrieve`, `query_plan`, `get`, `multi_get`, `find_similar`, `find_causal_links`, `timeline`, `memory_evolution_status`, `session_log`, `memory_pin`, `memory_snooze`, `memory_forget`, `lifecycle_status`, `lifecycle_sweep`, `lifecycle_restore`, `status`, `reindex`, `index_stats`, `build_graphs`.
|
|
46
|
+
|
|
47
|
+
## Populating a vault
|
|
48
|
+
|
|
49
|
+
Use `vault_sync` to index content into a named vault:
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
vault_sync(vault="work", content_root="~/projects/work-notes", pattern="**/*.md")
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
`vault_sync` includes restricted-path validation — it rejects sensitive directories like `/etc/`, `/root/`, `.ssh`, `.env`, `credentials`, `.aws`, `.kube`.
|
|
56
|
+
|
|
57
|
+
## Listing vaults
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
list_vaults()
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Returns configured vault names and paths. Empty in single-vault mode.
|
|
64
|
+
|
|
65
|
+
## Implementation details
|
|
66
|
+
|
|
67
|
+
- Named vault stores are cached in memory (`Map<string, Store>`) and reused across tool calls
|
|
68
|
+
- All cached stores are closed on MCP server shutdown (SIGINT/SIGTERM)
|
|
69
|
+
- Each vault is an independent SQLite database with its own documents, embeddings, graphs, and sessions
|
|
70
|
+
- WAL mode + `busy_timeout=5000ms` ensures safe concurrent access
|
|
71
|
+
- Hooks always operate on the default vault — vault selection is only available through MCP tools and REST API
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# Contributing to ClawMem
|
|
2
|
+
|
|
3
|
+
Development guide for the ClawMem memory engine (TypeScript on Bun, SQLite vector search, Claude Code hooks, MCP server).
|
|
4
|
+
|
|
5
|
+
## Development setup
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
git clone https://github.com/yoloshii/clawmem.git
|
|
9
|
+
cd clawmem
|
|
10
|
+
bun install
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Running tests
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
bun test # All tests
|
|
17
|
+
bun test tests/unit # Unit tests only
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Tests use in-memory SQLite databases and don't require GPU services.
|
|
21
|
+
|
|
22
|
+
## Type checking
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npx tsc --noEmit
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Must pass with zero errors on source files.
|
|
29
|
+
|
|
30
|
+
## Project structure
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
src/
|
|
34
|
+
clawmem.ts CLI entry point
|
|
35
|
+
mcp.ts MCP server
|
|
36
|
+
server.ts REST API server
|
|
37
|
+
store.ts SQLite store (documents, vectors, relations)
|
|
38
|
+
llm.ts LLM abstraction (embedding, generation, reranking)
|
|
39
|
+
config.ts Vault configuration, profiles, lifecycle policy
|
|
40
|
+
memory.ts Composite scoring (SAME)
|
|
41
|
+
search-utils.ts RRF, enrichment, ranking utilities
|
|
42
|
+
mmr.ts Maximal Marginal Relevance diversity filter
|
|
43
|
+
intent.ts Intent classification (MAGMA)
|
|
44
|
+
graph-traversal.ts Adaptive multi-hop traversal
|
|
45
|
+
indexer.ts Collection scanner, document indexer
|
|
46
|
+
collections.ts Collection configuration loader
|
|
47
|
+
validation.ts Input validation helpers
|
|
48
|
+
normalize.ts Conversation format normalizer (Claude, ChatGPT, Slack, plain text)
|
|
49
|
+
recall-buffer.ts Recall event writing (direct SQLite write during context-surfacing)
|
|
50
|
+
recall-attribution.ts Per-turn reference attribution (transcript segmentation + usage linkage)
|
|
51
|
+
limits.ts Constants (max path length, query length)
|
|
52
|
+
errors.ts Error types
|
|
53
|
+
promptguard.ts Prompt injection sanitization
|
|
54
|
+
retrieval-gate.ts Adaptive retrieval filtering
|
|
55
|
+
hooks.ts Hook utilities (output format, dedup, logging)
|
|
56
|
+
hooks/
|
|
57
|
+
context-surfacing.ts UserPromptSubmit hook
|
|
58
|
+
decision-extractor.ts Stop hook (observations)
|
|
59
|
+
handoff-generator.ts Stop hook (session summary)
|
|
60
|
+
feedback-loop.ts Stop hook (reference tracking)
|
|
61
|
+
precompact-extract.ts PreCompact hook
|
|
62
|
+
session-bootstrap.ts SessionStart hook (optional)
|
|
63
|
+
staleness-check.ts SessionStart hook (optional)
|
|
64
|
+
curator-nudge.ts SessionStart hook
|
|
65
|
+
openclaw/
|
|
66
|
+
index.ts Plugin entry point (registers as kind=memory, wires hook handlers)
|
|
67
|
+
engine.ts Retrieval/extraction engine (invoked from hook handlers in index.ts)
|
|
68
|
+
shell.ts Shell-out transport utilities
|
|
69
|
+
tools.ts REST API agent tools
|
|
70
|
+
openclaw.plugin.json Legacy plugin manifest (still shipped; parsed at runtime)
|
|
71
|
+
package.json OpenClaw v2026.4.11+ discovery manifest (openclaw.extensions)
|
|
72
|
+
tests/
|
|
73
|
+
unit/ Unit tests
|
|
74
|
+
integration/ Integration tests (when present)
|
|
75
|
+
docs/ Documentation (this folder)
|
|
76
|
+
bin/
|
|
77
|
+
clawmem Wrapper script (sets env defaults)
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Pull request guidelines
|
|
81
|
+
|
|
82
|
+
1. **Describe what and why** — not just what changed, but why
|
|
83
|
+
2. **Include test coverage** — new features need tests, bug fixes should include a regression test
|
|
84
|
+
3. **Type check clean** — `npx tsc --noEmit` must pass
|
|
85
|
+
4. **All tests pass** — `bun test` must pass
|
|
86
|
+
5. **Keep changes focused** — one feature or fix per PR
|
|
87
|
+
|
|
88
|
+
## What gets indexed
|
|
89
|
+
|
|
90
|
+
Only `.md` files. Never add indexing for binary files, source code, or credentials.
|
|
91
|
+
|
|
92
|
+
## Security considerations
|
|
93
|
+
|
|
94
|
+
- Never index or expose credential files (`.env`, `*secrets*`, `*credentials*`)
|
|
95
|
+
- `vault_sync` validates paths against a deny-list — don't weaken it
|
|
96
|
+
- Prompt injection sanitization (`promptguard.ts`) strips control sequences from injected context
|
|
97
|
+
- Bearer token auth on REST API when `CLAWMEM_API_TOKEN` is set
|
|
98
|
+
|
|
99
|
+
## License
|
|
100
|
+
|
|
101
|
+
MIT. See [LICENSE](../LICENSE).
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# Cloud embedding for ClawMem
|
|
2
|
+
|
|
3
|
+
By default, ClawMem embeds AI agent memory locally via `llama-server` or in-process `node-llama-cpp` fallback (Metal on Apple Silicon, Vulkan where available, CPU as last resort). With GPU acceleration the fallback is fast; CPU-only is significantly slower. As an alternative, you can use a cloud embedding provider instead of running models on your machine.
|
|
4
|
+
|
|
5
|
+
## Supported providers
|
|
6
|
+
|
|
7
|
+
| Provider | URL | Model | Dimensions |
|
|
8
|
+
|----------|-----|-------|-----------|
|
|
9
|
+
| Jina AI | `https://api.jina.ai` | `jina-embeddings-v5-text-small` | 1024 |
|
|
10
|
+
| OpenAI | `https://api.openai.com` | `text-embedding-3-small` | 1536 |
|
|
11
|
+
| Voyage AI | `https://api.voyageai.com` | `voyage-4-large` | 1024 |
|
|
12
|
+
| Cohere | `https://api.cohere.com` | `embed-v4.0` | 1024 |
|
|
13
|
+
|
|
14
|
+
All providers use the OpenAI-compatible `/v1/embeddings` endpoint with Bearer token auth.
|
|
15
|
+
|
|
16
|
+
## Configuration
|
|
17
|
+
|
|
18
|
+
Copy `.env.example` to `.env` and set your provider credentials:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
cp .env.example .env
|
|
22
|
+
# Edit .env:
|
|
23
|
+
CLAWMEM_EMBED_URL=https://api.jina.ai
|
|
24
|
+
CLAWMEM_EMBED_API_KEY=jina_your-key-here
|
|
25
|
+
CLAWMEM_EMBED_MODEL=jina-embeddings-v5-text-small
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Or export them in your shell before running `clawmem`.
|
|
29
|
+
|
|
30
|
+
**Precedence:** shell environment > `.env` file > `bin/clawmem` wrapper defaults. The wrapper sources `.env` from the project root before applying its defaults, so `.env` values override defaults but explicit shell exports still win.
|
|
31
|
+
|
|
32
|
+
## Cloud mode features
|
|
33
|
+
|
|
34
|
+
When `CLAWMEM_EMBED_API_KEY` is set, cloud mode activates:
|
|
35
|
+
|
|
36
|
+
- **Batch embedding** — 50 fragments per API request (vs 1 per request for local). Reduces total API calls ~50×.
|
|
37
|
+
- **Provider-specific retrieval params** — Auto-detected from your `CLAWMEM_EMBED_URL`. Documents and queries get different params for optimal retrieval quality (see table below).
|
|
38
|
+
- **Server-side truncation** — Provider-appropriate truncation params sent automatically. Oversized inputs are truncated server-side instead of returning errors.
|
|
39
|
+
- **Adaptive TPM-aware pacing** — Delays between batches computed from actual token usage to stay within your tier's tokens-per-minute limit. No hardcoded tier assumptions.
|
|
40
|
+
- **Retry with jitter** — 429 responses trigger retries with exponential backoff (5s → 10s → 20s, max 3 retries) plus random jitter to prevent synchronized retry storms.
|
|
41
|
+
|
|
42
|
+
### Provider-specific parameters
|
|
43
|
+
|
|
44
|
+
ClawMem auto-detects your provider from the URL and sends the right params:
|
|
45
|
+
|
|
46
|
+
| Provider | Document embedding | Query embedding | Truncation | Extra |
|
|
47
|
+
|----------|-------------------|-----------------|------------|-------|
|
|
48
|
+
| Jina AI | `task: "retrieval.passage"` | `task: "retrieval.query"` | `truncate: true` | |
|
|
49
|
+
| Voyage AI | `input_type: "document"` | `input_type: "query"` | Default (true) | |
|
|
50
|
+
| Cohere | `input_type: "search_document"` | `input_type: "search_query"` | `truncate: "END"` | |
|
|
51
|
+
| OpenAI | Symmetric (none) | Symmetric (none) | None (8192 max) | `dimensions` via `CLAWMEM_EMBED_DIMENSIONS` |
|
|
52
|
+
|
|
53
|
+
No configuration needed — just set `CLAWMEM_EMBED_URL` to the provider and the correct params are applied. For OpenAI's `text-embedding-3-*` models, optionally set `CLAWMEM_EMBED_DIMENSIONS` to reduce output dimensions (e.g. `512` or `1024`).
|
|
54
|
+
|
|
55
|
+
**Note on OpenAI truncation:** OpenAI does not auto-truncate — inputs exceeding 8192 tokens return an error. ClawMem's default chunk size is ~800 tokens, well under this limit, so this is not an issue in practice.
|
|
56
|
+
|
|
57
|
+
## Rate limiting
|
|
58
|
+
|
|
59
|
+
Cloud providers enforce rate limits on both requests-per-minute (RPM) and tokens-per-minute (TPM). TPM is typically the binding constraint for embedding.
|
|
60
|
+
|
|
61
|
+
Set `CLAWMEM_EMBED_TPM_LIMIT` to match your provider tier:
|
|
62
|
+
|
|
63
|
+
| Tier | RPM | TPM | `CLAWMEM_EMBED_TPM_LIMIT` |
|
|
64
|
+
|------|-----|-----|--------------------------|
|
|
65
|
+
| Jina Free | 100 | 100,000 | `100000` (default) |
|
|
66
|
+
| Jina Paid | 500 | 2,000,000 | `2000000` |
|
|
67
|
+
| Jina Premium | 5,000 | 50,000,000 | `50000000` |
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
# Example: paid tier
|
|
71
|
+
export CLAWMEM_EMBED_TPM_LIMIT=2000000
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The adaptive pacer computes delay as `(batchTokens / (TPM_LIMIT × 0.85)) × 60s`, using actual token counts from the API response when available, falling back to character-based estimation. The 0.85 safety factor leaves headroom for retries.
|
|
75
|
+
|
|
76
|
+
## Truncation behavior
|
|
77
|
+
|
|
78
|
+
- **Local embedding** (no API key): Input truncated to `CLAWMEM_EMBED_MAX_CHARS` (default 6000) before sending. This prevents oversized inputs from exceeding the model's token context.
|
|
79
|
+
- **Cloud embedding** (API key set): Client-side truncation is skipped. Server-side truncation is requested via `truncate: true`.
|
|
80
|
+
|
|
81
|
+
To override the local truncation limit:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
export CLAWMEM_EMBED_MAX_CHARS=4000 # for models with smaller context
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Localhost warning
|
|
88
|
+
|
|
89
|
+
If you set `CLAWMEM_EMBED_API_KEY` but your `CLAWMEM_EMBED_URL` points to localhost or 127.0.0.1, ClawMem prints a one-time warning. This catches accidental configurations where an API key is sent to a local server. If you're using a local API gateway intentionally, the warning is safe to ignore.
|
|
90
|
+
|
|
91
|
+
## Mixing local and cloud
|
|
92
|
+
|
|
93
|
+
The LLM (query expansion) and reranker always use local `llama-server` or in-process `node-llama-cpp` fallback. Only embedding supports cloud providers. This means:
|
|
94
|
+
|
|
95
|
+
- **Embedding** — local GPU, cloud API, or in-process via `node-llama-cpp` (Metal/Vulkan/CPU — fast with GPU acceleration, slow on CPU-only)
|
|
96
|
+
- **LLM** — local GPU, falls back to in-process `node-llama-cpp`
|
|
97
|
+
- **Reranker** — local GPU, falls back to in-process `node-llama-cpp`
|
|
98
|
+
|
|
99
|
+
**Note:** In-process fallback is silent — if a GPU server crashes, there is no warning. With Metal/Vulkan the fallback is fast; on CPU-only it is significantly slower. Set `CLAWMEM_NO_LOCAL_MODELS=true` to fail fast instead, or use [systemd services](systemd-services.md) to keep servers running.
|
|
100
|
+
|
|
101
|
+
## Model recommendations
|
|
102
|
+
|
|
103
|
+
**Default (QMD native combo, any GPU or in-process):** EmbeddingGemma-300M-Q8_0 (314MB, 768d) + qwen3-reranker-0.6B (600MB) + qmd-query-expansion-1.7B (~1.1GB). All three auto-download via `node-llama-cpp` if no server is running (Metal on Apple Silicon, Vulkan where available, CPU as last resort). Fast with GPU acceleration; significantly slower on CPU-only.
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
llama-server -m embeddinggemma-300M-Q8_0.gguf \
|
|
107
|
+
--embeddings --port 8088 --host 0.0.0.0 -ngl 99 -c 2048 --batch-size 2048
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
**SOTA upgrade (16GB+ GPU):** **ZeroEntropy zembed-1** (2560 dimensions, 32K context, SOTA retrieval quality, ~4.4GB VRAM) paired with the **zerank-2 seq-cls reranker sidecar** (distillation-paired via zELO). **CC-BY-NC-4.0** — non-commercial only.
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
llama-server -m zembed-1-Q4_K_M.gguf \
|
|
114
|
+
--embeddings --port 8088 --host 0.0.0.0 -ngl 99 -c 8192 -b 2048 -ub 2048
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
The reranker is **not** a GGUF — serve it via the seq-cls sidecar (transformers, bf16): see [`extras/rerankers/zerank-2-seq/`](../../extras/rerankers/zerank-2-seq/). The old `zerank-2-Q4_K_M` GGUF is deprecated (llama.cpp drops its score head → near-zero, uninformative scores; final ordering stays RRF-dominated).
|
|
118
|
+
|
|
119
|
+
For cloud, **Jina AI `jina-embeddings-v5-text-small`** is recommended (1024 dimensions, 32K context, task-specific LoRA adapters for retrieval).
|
|
120
|
+
|
|
121
|
+
## Switching embedding models
|
|
122
|
+
|
|
123
|
+
When changing to a model with different output dimensions (e.g. 768d → 2560d), a full re-embed is required:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
clawmem embed --force
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
This clears all existing vectors and rebuilds with the new model's dimensions. The vector table is automatically recreated with the correct dimension size on the first embedded fragment.
|
|
130
|
+
|
|
131
|
+
**Important notes:**
|
|
132
|
+
- `--force` is safe to interrupt and resume — embed is idempotent and skips already-embedded documents
|
|
133
|
+
- `-ub` must equal `-b` on `llama-server` for embedding/reranking models (non-causal attention). Omitting `-ub` causes assertion crashes.
|
|
134
|
+
- Set `-c` (context) high enough for your largest fragments. zembed-1 supports 32K; `-c 8192` is recommended.
|