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 +1 -1
- package/src/clawmem.ts +14 -0
- package/src/hooks/staleness-check.ts +27 -0
- package/src/hooks.ts +1 -0
- package/src/openclaw/index.ts +33 -1
package/package.json
CHANGED
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
|
|
package/src/openclaw/index.ts
CHANGED
|
@@ -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:
|
|
138
|
+
prompt: searchPrompt,
|
|
107
139
|
});
|
|
108
140
|
|
|
109
141
|
if (surfacingResult.exitCode === 0) {
|