clawmem 0.1.7 → 0.1.8

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clawmem",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "description": "On-device context engine and memory for AI agents. Claude Code and OpenClaw. Hooks + MCP server + hybrid RAG search.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/clawmem.ts CHANGED
@@ -1237,8 +1237,22 @@ async function cmdWatch() {
1237
1237
  // Skill vault not configured — skip
1238
1238
  }
1239
1239
 
1240
+ // Periodic WAL checkpoint: the watcher holds a long-lived DB connection which
1241
+ // prevents SQLite auto-checkpoint from shrinking the WAL file. Without this,
1242
+ // the WAL grows unbounded (observed 77MB+), slowing every concurrent DB access
1243
+ // (hooks, MCP) and eventually causing UserPromptSubmit hook timeouts.
1244
+ const WAL_CHECKPOINT_INTERVAL = 5 * 60 * 1000; // 5 minutes
1245
+ const checkpointTimer = setInterval(() => {
1246
+ try {
1247
+ s.db.exec("PRAGMA wal_checkpoint(PASSIVE)");
1248
+ } catch {
1249
+ // Checkpoint failed (busy) — will retry next interval
1250
+ }
1251
+ }, WAL_CHECKPOINT_INTERVAL);
1252
+
1240
1253
  // Keep running until Ctrl+C
1241
1254
  process.on("SIGINT", () => {
1255
+ clearInterval(checkpointTimer);
1242
1256
  watcher.close();
1243
1257
  skillWatcher?.close();
1244
1258
  closeStore();
@@ -51,6 +51,33 @@ export async function stalenessCheck(
51
51
  }
52
52
  }
53
53
 
54
+ // Auto-archive if lifecycle policy is configured (runs regardless of stale report results)
55
+ try {
56
+ const { loadVaultConfig } = await import("../config.ts");
57
+ const config = loadVaultConfig();
58
+ if (config.lifecycle) {
59
+ const candidates = store.getArchiveCandidates(config.lifecycle);
60
+ if (candidates.length > 0 && !config.lifecycle.dry_run) {
61
+ const archived = store.archiveDocuments(candidates.map(c => c.id));
62
+ if (archived > 0 && input.sessionId) {
63
+ store.insertUsage({
64
+ sessionId: input.sessionId,
65
+ timestamp: now.toISOString(),
66
+ hookName: "lifecycle-archive",
67
+ injectedPaths: candidates.map(c => `${c.collection}/${c.path}`),
68
+ estimatedTokens: 0,
69
+ wasReferenced: 0,
70
+ });
71
+ }
72
+ }
73
+ if (config.lifecycle.purge_after_days && !config.lifecycle.dry_run) {
74
+ store.purgeArchivedDocuments(config.lifecycle.purge_after_days);
75
+ }
76
+ }
77
+ } catch {
78
+ // Fail-open: lifecycle errors never block the hook
79
+ }
80
+
54
81
  if (allStale.size === 0) return makeEmptyOutput("staleness-check");
55
82
 
56
83
  // Build context within budget
package/src/hooks.ts CHANGED
@@ -84,6 +84,7 @@ const HOOK_EVENT_MAP: Record<string, string | null> = {
84
84
  "feedback-loop": null, // Stop — no hookSpecificOutput
85
85
  "precompact-extract": null, // PreCompact — side-effect only, no context injection
86
86
  "postcompact-inject": "SessionStart", // SessionStart(compact) — injects additionalContext
87
+ "curator-nudge": "SessionStart", // SessionStart — surfaces curator report actions
87
88
  "pretool-inject": null, // PreToolUse — disabled (cannot inject additionalContext; E13 folded into context-surfacing)
88
89
  };
89
90
 
@@ -19,6 +19,36 @@ import { resolveClawMemBin, execHook, parseHookOutput, extractContext } from "./
19
19
  import type { ClawMemConfig } from "./shell.js";
20
20
  import { createTools } from "./tools.js";
21
21
 
22
+ // =============================================================================
23
+ // Prompt Cleaning (strips OpenClaw noise for better retrieval)
24
+ // Pattern extracted from memory-core-plus (MIT, aloong-planet)
25
+ // =============================================================================
26
+
27
+ /**
28
+ * Strip OpenClaw-specific noise from the user prompt before using it as a
29
+ * search query. Gateway prompts contain metadata, system events, timestamps,
30
+ * and previously injected context that degrade embedding/BM25 quality.
31
+ */
32
+ function cleanPromptForSearch(prompt: string): string {
33
+ let cleaned = prompt;
34
+ // Strip previously injected vault-context (avoid re-searching our own output)
35
+ cleaned = cleaned.replace(/<vault-context>[\s\S]*?<\/vault-context>/g, "");
36
+ cleaned = cleaned.replace(/<vault-routing>[\s\S]*?<\/vault-routing>/g, "");
37
+ cleaned = cleaned.replace(/<vault-session>[\s\S]*?<\/vault-session>/g, "");
38
+ // Strip OpenClaw sender metadata block
39
+ cleaned = cleaned.replace(/Sender\s*\(untrusted metadata\)\s*:\s*```json\n[\s\S]*?```/g, "");
40
+ cleaned = cleaned.replace(/Sender\s*\(untrusted metadata\)\s*:\s*\{[\s\S]*?\}\s*/g, "");
41
+ // Strip OpenClaw runtime context blocks
42
+ cleaned = cleaned.replace(/OpenClaw runtime context \(internal\):[\s\S]*?(?=\n\n|\n?$)/g, "");
43
+ // Strip "System: ..." single-line event entries
44
+ cleaned = cleaned.replace(/^System:.*$/gm, "");
45
+ // Strip timestamp prefixes e.g. "[Sat 2026-03-14 16:19 GMT+8] "
46
+ cleaned = cleaned.replace(/^\[.*?GMT[+-]\d+\]\s*/gm, "");
47
+ // Collapse excessive whitespace
48
+ cleaned = cleaned.replace(/\n{3,}/g, "\n\n").trim();
49
+ return cleaned || prompt;
50
+ }
51
+
22
52
  // =============================================================================
23
53
  // Plugin Definition
24
54
  // =============================================================================
@@ -101,9 +131,11 @@ const clawmemPlugin = {
101
131
  }
102
132
 
103
133
  // Every turn: run context-surfacing for prompt-aware retrieval
134
+ // Clean the prompt to remove OpenClaw noise before search
135
+ const searchPrompt = cleanPromptForSearch(event.prompt);
104
136
  const surfacingResult = await execHook(cfg, "context-surfacing", {
105
137
  session_id: sessionId,
106
- prompt: event.prompt,
138
+ prompt: searchPrompt,
107
139
  });
108
140
 
109
141
  if (surfacingResult.exitCode === 0) {