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.
@@ -0,0 +1,332 @@
1
+ # Systemd services for ClawMem
2
+
3
+ Keep ClawMem's AI agent memory services running automatically with systemd user units. This is important for GPU setups — if a llama-server crashes, ClawMem silently falls back to in-process inference via `node-llama-cpp` (Metal on Apple Silicon, Vulkan where available, CPU as last resort). With GPU acceleration (Metal/Vulkan) the fallback is fast; on CPU-only systems it is significantly slower. Systemd's `Restart=on-failure` ensures servers come back up automatically. To disable silent fallback entirely, set `CLAWMEM_NO_LOCAL_MODELS=true`.
4
+
5
+ ## Watcher service
6
+
7
+ Monitors collections for file changes and re-indexes automatically:
8
+
9
+ ```bash
10
+ cat > ~/.config/systemd/user/clawmem-watcher.service << 'EOF'
11
+ [Unit]
12
+ Description=ClawMem file watcher — auto-indexes on .md changes
13
+ After=default.target
14
+
15
+ [Service]
16
+ Type=simple
17
+ ExecStart=%h/clawmem/bin/clawmem watch
18
+ Restart=on-failure
19
+ RestartSec=10
20
+
21
+ [Install]
22
+ WantedBy=default.target
23
+ EOF
24
+ ```
25
+
26
+ ## Embed timer
27
+
28
+ Daily embedding sweep at 04:00 UTC:
29
+
30
+ ```bash
31
+ cat > ~/.config/systemd/user/clawmem-embed.service << 'EOF'
32
+ [Unit]
33
+ Description=ClawMem embedding sweep
34
+
35
+ [Service]
36
+ Type=oneshot
37
+ ExecStart=%h/clawmem/bin/clawmem embed
38
+ EOF
39
+
40
+ cat > ~/.config/systemd/user/clawmem-embed.timer << 'EOF'
41
+ [Unit]
42
+ Description=ClawMem daily embedding sweep
43
+
44
+ [Timer]
45
+ OnCalendar=*-*-* 04:00:00
46
+ Persistent=true
47
+ RandomizedDelaySec=300
48
+
49
+ [Install]
50
+ WantedBy=timers.target
51
+ EOF
52
+ ```
53
+
54
+ ## Enable and start
55
+
56
+ ```bash
57
+ mkdir -p ~/.config/systemd/user
58
+ systemctl --user daemon-reload
59
+ systemctl --user enable --now clawmem-watcher.service clawmem-embed.timer
60
+
61
+ # Persist across reboots (start without login)
62
+ loginctl enable-linger $(whoami)
63
+ ```
64
+
65
+ ## Verify
66
+
67
+ ```bash
68
+ systemctl --user status clawmem-watcher.service
69
+ systemctl --user status clawmem-embed.timer
70
+ ```
71
+
72
+ ## Background maintenance workers (v0.8.2)
73
+
74
+ ClawMem ships two background workers that improve retrieval quality over time:
75
+
76
+ - **Light consolidation lane** — every 5-10 min, enriches documents missing A-MEM metadata (Phase 1), merges near-duplicate observations (Phase 2), and synthesizes deductive observations from related decisions (Phase 3). Off by default.
77
+ - **Heavy maintenance lane** — runs on a longer interval *only inside a configurable hour window*, batches stale-first work (least-recently-recalled documents first), and journals every attempt. Designed for overnight maintenance on large vaults. Off by default.
78
+
79
+ As of v0.8.2 the canonical host for both lanes is the long-lived `clawmem-watcher.service` you just set up, so the heavy lane's quiet window actually sees a live worker every night regardless of whether any agent session is open. The per-session stdio MCP host (`clawmem mcp`) retains the same env-var gates as a fallback for non-watcher deployments, but emits a warning when heavy lane is enabled there.
80
+
81
+ Both lanes share DB-backed `worker_leases` exclusivity, so running multiple host processes against the same vault is safe — only one worker can hold the lease for each lane at a time.
82
+
83
+ ### Enable via systemd drop-in
84
+
85
+ Recommended approach: `systemctl --user edit clawmem-watcher.service` and paste the following. This creates `~/.config/systemd/user/clawmem-watcher.service.d/override.conf` without editing the main unit file, so a future `clawmem` upgrade that touches the unit file does not clobber your tuning.
86
+
87
+ ```ini
88
+ [Service]
89
+ # Light consolidation lane — drains enrichment backlog and runs Phase 2/3
90
+ # consolidation. Hosted inside the long-lived watcher process, so it ticks
91
+ # whenever the watcher is up. v0.8.2+ wraps every tick in a worker_leases
92
+ # row so multi-host deployments are safe.
93
+ Environment=CLAWMEM_ENABLE_CONSOLIDATION=true
94
+ Environment=CLAWMEM_CONSOLIDATION_INTERVAL=600000
95
+
96
+ # Heavy maintenance lane — quiet-hours stale-first batch consolidation +
97
+ # deductive synthesis. Off-hours window in LOCAL time (not UTC).
98
+ Environment=CLAWMEM_HEAVY_LANE=true
99
+ Environment=CLAWMEM_HEAVY_LANE_INTERVAL=1800000
100
+ Environment=CLAWMEM_HEAVY_LANE_WINDOW_START=2
101
+ Environment=CLAWMEM_HEAVY_LANE_WINDOW_END=6
102
+ ```
103
+
104
+ Then reload + restart:
105
+
106
+ ```bash
107
+ systemctl --user daemon-reload
108
+ systemctl --user restart clawmem-watcher.service
109
+ journalctl --user -u clawmem-watcher.service -n 30 --no-pager | grep -E "Starting (consolidation|heavy)"
110
+ # Expected:
111
+ # [watch] Starting consolidation worker (light lane, interval=600000ms)
112
+ # [consolidation] Worker started
113
+ # [watch] Starting heavy maintenance lane worker
114
+ # [heavy-lane] Starting worker (interval=1800000ms, window=2-6, ...)
115
+ ```
116
+
117
+ ### Tuning for your usage pattern
118
+
119
+ The defaults (`CLAWMEM_CONSOLIDATION_INTERVAL=600000` / 10 min; heavy lane `CLAWMEM_HEAVY_LANE_INTERVAL=1800000` / 30 min, window 02:00-06:00 local) suit a single-developer workstation with overnight idle time. Adjust based on your actual usage:
120
+
121
+ | Pattern | Light interval | Heavy window | Notes |
122
+ |---|---|---|---|
123
+ | **Single workstation, overnight idle** | 600000 (10 min) | 02:00-06:00 local | Default. Fine for vaults with up to a few thousand docs. |
124
+ | **Always-on workstation, no idle window** | 900000 (15 min) | unset (no window — runs every interval) | Heavy lane will fire every 30 min regardless of hour. The query-rate gate (`CLAWMEM_HEAVY_LANE_MAX_USAGES=30`) skips ticks during active sessions, so it self-throttles. |
125
+ | **Shared multi-user server** | 1200000 (20 min) | 03:00-05:00 local | Conservative — minimize background CPU/GPU contention. |
126
+ | **Large vault (>10k docs), heavy ingestion** | 600000 (10 min) | 01:00-07:00 local | Wider quiet window to give Phase 2 + Phase 3 time to drain stale-first batches. Bump `CLAWMEM_HEAVY_LANE_OBS_LIMIT=200` and `CLAWMEM_HEAVY_LANE_DED_LIMIT=80` to process more per tick. |
127
+ | **Laptop / ephemeral host** | unset (off) | unset (off) | Workers do not survive sleep/suspend cleanly. Run consolidation manually via `clawmem consolidate` instead, or accept the per-session-MCP fallback. |
128
+
129
+ **Minimum intervals are clamped in code:** light lane is forced to ≥15 s (`CLAWMEM_CONSOLIDATION_INTERVAL=15000`), heavy lane to ≥30 s (`CLAWMEM_HEAVY_LANE_INTERVAL=30000`). Lower values are silently bumped up.
130
+
131
+ **Quiet window semantics:** start/end hours are in **local time** (`new Date().getHours()`). Both bounds inclusive at start, exclusive at end. Supports midnight wrap (`START=22 END=6` = 22:00-06:00 across midnight). Either bound unset → no window (always run).
132
+
133
+ ### What to expect after the first restart
134
+
135
+ Assuming you set both env vars and restarted the watcher around midday:
136
+
137
+ - **+ a few seconds:** watcher logs `[consolidation] Worker started` and `[heavy-lane] Starting worker (...)`. Workers are scheduled but no tick has fired yet — `setInterval` waits one full interval before the first call.
138
+ - **+ light interval (e.g. ~10 min):** first light-lane tick fires. Phase 1 enriches up to 3 unenriched docs per tick, so a backlog of N unenriched docs takes roughly `N/3 × interval` to drain. A 100-doc backlog at 10-min interval drains in ~5.5 hours.
139
+ - **+ heavy interval (e.g. ~30 min):** first heavy-lane tick fires. If you are *outside* the quiet window, the lane journals `(phase=gate, status=skipped, reason=outside_window)` to `maintenance_runs` and waits for the next interval. This is normal and expected during the day.
140
+ - **First night inside the quiet window:** heavy lane runs in earnest. Phase 2 (consolidation) and Phase 3 (deductive synthesis) each get their own `maintenance_runs` row per tick with `status=completed` and per-phase metrics.
141
+
142
+ ### Monitoring
143
+
144
+ ```bash
145
+ # Light-lane drainage progress: how much enrichment backlog is left?
146
+ sqlite3 -readonly ~/.cache/clawmem/index.sqlite \
147
+ "SELECT COUNT(*) AS total,
148
+ SUM(CASE WHEN amem_keywords IS NULL OR amem_keywords='' THEN 1 ELSE 0 END) AS unenriched
149
+ FROM documents WHERE active=1"
150
+
151
+ # Heavy-lane journal: every scheduled attempt, including skipped ones
152
+ sqlite3 -readonly ~/.cache/clawmem/index.sqlite \
153
+ "SELECT lane, phase, status, reason, started_at, finished_at
154
+ FROM maintenance_runs
155
+ WHERE lane = 'heavy'
156
+ ORDER BY id DESC LIMIT 10"
157
+
158
+ # Why did the heavy lane skip its most recent tick?
159
+ sqlite3 -readonly ~/.cache/clawmem/index.sqlite \
160
+ "SELECT status, reason, started_at FROM maintenance_runs
161
+ WHERE lane = 'heavy' AND phase = 'gate' AND status = 'skipped'
162
+ ORDER BY id DESC LIMIT 5"
163
+
164
+ # Phase 2 / Phase 3 throughput over the last 7 days
165
+ sqlite3 -readonly ~/.cache/clawmem/index.sqlite \
166
+ "SELECT phase, status, COUNT(*), SUM(processed_count), SUM(created_count)
167
+ FROM maintenance_runs
168
+ WHERE lane = 'heavy' AND started_at > datetime('now', '-7 days')
169
+ GROUP BY phase, status"
170
+
171
+ # Are any leases stuck? (Should be empty unless a worker is mid-tick right now)
172
+ sqlite3 -readonly ~/.cache/clawmem/index.sqlite \
173
+ "SELECT worker_name, acquired_at, expires_at FROM worker_leases"
174
+
175
+ # Live worker activity in the watcher service
176
+ journalctl --user -u clawmem-watcher.service -n 50 --no-pager | \
177
+ grep -E "(consolidation|heavy-lane|Worker)"
178
+ ```
179
+
180
+ ### Rollback
181
+
182
+ To temporarily disable the workers without removing the drop-in, set both env vars to anything other than `"true"`:
183
+
184
+ ```bash
185
+ systemctl --user edit clawmem-watcher.service
186
+ # Change the values to "false" then save
187
+ systemctl --user daemon-reload
188
+ systemctl --user restart clawmem-watcher.service
189
+ ```
190
+
191
+ To remove the drop-in entirely:
192
+
193
+ ```bash
194
+ rm ~/.config/systemd/user/clawmem-watcher.service.d/override.conf
195
+ systemctl --user daemon-reload
196
+ systemctl --user restart clawmem-watcher.service
197
+ ```
198
+
199
+ The vault state (existing `consolidated_observations`, deductive documents, `maintenance_runs` history) is preserved. Disabling the workers only stops further background work — it does not roll back any consolidation that already happened.
200
+
201
+ For the architectural deep dive on how the workers operate, see [docs/concepts/architecture.md](../concepts/architecture.md) sections "Heavy maintenance lane (v0.8.0)" and "Dual-host worker architecture (v0.8.2)".
202
+
203
+ ## Remote GPU
204
+
205
+ If GPU services run on a different machine, add environment overrides to both services:
206
+
207
+ ```ini
208
+ [Service]
209
+ Environment=CLAWMEM_EMBED_URL=http://gpu-host:8088
210
+ Environment=CLAWMEM_LLM_URL=http://gpu-host:8089
211
+ Environment=CLAWMEM_LLM_MODEL=qwen3
212
+ Environment=CLAWMEM_RERANK_URL=http://gpu-host:8090
213
+ ```
214
+
215
+ Or create a drop-in override:
216
+
217
+ ```bash
218
+ mkdir -p ~/.config/systemd/user/clawmem-watcher.service.d
219
+ cat > ~/.config/systemd/user/clawmem-watcher.service.d/gpu.conf << 'EOF'
220
+ [Service]
221
+ Environment=CLAWMEM_EMBED_URL=http://gpu-host:8088
222
+ Environment=CLAWMEM_LLM_URL=http://gpu-host:8089
223
+ Environment=CLAWMEM_LLM_MODEL=qwen3
224
+ Environment=CLAWMEM_RERANK_URL=http://gpu-host:8090
225
+ EOF
226
+ systemctl --user daemon-reload
227
+ systemctl --user restart clawmem-watcher.service
228
+ ```
229
+
230
+ ## REST API service (for OpenClaw)
231
+
232
+ Required for OpenClaw agent tools and remote access. Optional for local MCP clients like Claude Code (which use MCP stdio directly).
233
+
234
+ ```bash
235
+ cat > ~/.config/systemd/user/clawmem-serve.service << 'EOF'
236
+ [Unit]
237
+ Description=ClawMem REST API server
238
+ After=default.target
239
+
240
+ [Service]
241
+ Type=simple
242
+ ExecStart=%h/clawmem/bin/clawmem serve --port 7438
243
+ Restart=on-failure
244
+ RestartSec=5
245
+
246
+ [Install]
247
+ WantedBy=default.target
248
+ EOF
249
+ ```
250
+
251
+ For authenticated access, add `Environment=CLAWMEM_API_TOKEN=your-secret` to the `[Service]` section.
252
+
253
+ Enable alongside the other services:
254
+
255
+ ```bash
256
+ systemctl --user enable --now clawmem-serve.service
257
+ ```
258
+
259
+ See the [REST API reference](../reference/rest-api.md) for endpoints and usage.
260
+
261
+ ## GPU service units
262
+
263
+ The default stack's three inference servers can also run as systemd services:
264
+
265
+ ```bash
266
+ # Example: embedding server
267
+ cat > ~/.config/systemd/user/clawmem-embed-server.service << 'EOF'
268
+ [Unit]
269
+ Description=ClawMem embedding server (zembed-1)
270
+ After=default.target
271
+
272
+ [Service]
273
+ Type=simple
274
+ ExecStart=/usr/local/bin/llama-server \
275
+ -m %h/models/zembed-1-Q4_K_M.gguf \
276
+ --embeddings --port 8088 --host 0.0.0.0 -ngl 99 -c 8192 -b 2048 -ub 2048
277
+ Restart=on-failure
278
+ RestartSec=5
279
+
280
+ [Install]
281
+ WantedBy=default.target
282
+ EOF
283
+ ```
284
+
285
+ Repeat for the LLM (port 8089) and the **default** reranker (port 8090 — `qwen3-reranker-0.6B` via `--reranking`) with their respective models and flags.
286
+
287
+ > **The SOTA reranker is not a `llama-server` unit.** The zerank-2 SOTA reranker runs as a transformers **sidecar** (a small container behind the same `/v1/rerank` contract), not a systemd `llama-server` instance — see [`extras/rerankers/zerank-2-seq/`](../../extras/rerankers/zerank-2-seq/). The old `zerank-2-Q4_K_M` GGUF served via `--reranking` is deprecated (llama.cpp drops zerank's score head → near-zero, uninformative scores).
288
+
289
+ ## Reranker health check (scheduled)
290
+
291
+ `clawmem rerank-health` probes the reranker for **discrimination** (not just liveness) and exits non-zero when it is degenerate — catching the failure mode where a mis-converted reranker (e.g. the deprecated zerank-2 GGUF, whose llama.cpp conversion drops the score head) returns HTTP 200 + valid JSON but near-zero, non-discriminating scores, silently collapsing the final ranking to RRF. The same probe runs inside `clawmem doctor`; this scheduled unit alerts proactively without query traffic. Schedule it when the reranker is a remote sidecar that could be redeployed/reverted out from under you.
292
+
293
+ ```bash
294
+ # clawmem-rerank-health.service — oneshot probe; exits 1 if the reranker is degenerate
295
+ cat > ~/.config/systemd/user/clawmem-rerank-health.service << 'EOF'
296
+ [Unit]
297
+ Description=ClawMem reranker discrimination health check
298
+
299
+ [Service]
300
+ Type=oneshot
301
+ # Hard outer bound — a hung reranker must not hang the check (the probe also times out per-request).
302
+ TimeoutStartSec=120
303
+ ExecStart=%h/clawmem/bin/clawmem rerank-health
304
+ # Alert on failure: point this at your notifier unit (ntfy, mail, etc.).
305
+ OnFailure=clawmem-rerank-health-alert@%n.service
306
+ EOF
307
+
308
+ # clawmem-rerank-health.timer — every 6h (the failure it guards is rare + infra-driven)
309
+ cat > ~/.config/systemd/user/clawmem-rerank-health.timer << 'EOF'
310
+ [Unit]
311
+ Description=ClawMem reranker health check (every 6h)
312
+
313
+ [Timer]
314
+ OnCalendar=*-*-* 00/6:00:00
315
+ Persistent=true
316
+ RandomizedDelaySec=300
317
+
318
+ [Install]
319
+ WantedBy=timers.target
320
+ EOF
321
+
322
+ systemctl --user daemon-reload
323
+ systemctl --user enable --now clawmem-rerank-health.timer
324
+ ```
325
+
326
+ For remote GPU setups, add `Environment=CLAWMEM_RERANK_URL=http://host:8090` (+ embed/LLM) to the `.service`. `OnFailure=` is the primary alert path; the `curator-nudge` SessionStart hook is a secondary "you missed the page" surface. Run `clawmem rerank-health` (or `--json`) manually any time to check on demand.
327
+
328
+ ## Notes
329
+
330
+ - `%h` in systemd units expands to the user's home directory
331
+ - The watcher does NOT embed — it only indexes. The embed timer handles embeddings separately.
332
+ - If clawmem is installed elsewhere, update `ExecStart` paths accordingly