clawmem 0.10.4 → 0.10.5

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.10.4",
3
+ "version": "0.10.5",
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": {
@@ -37,8 +37,8 @@
37
37
  * 4. REST API service (`clawmem serve`) lifecycle — unchanged.
38
38
  *
39
39
  * §14.3 critical correctness contract: `agent_end` is fire-and-forget at
40
- * `attempt.ts:3379-3402`. Precompact-extract MUST run inside
41
- * `handleBeforePromptBuild` (which IS awaited at `attempt.ts:2610`), gated
40
+ * `attempt.ts:3870-3892`. Precompact-extract MUST run inside
41
+ * `handleBeforePromptBuild` (which IS awaited at `attempt.ts:2973`), gated
42
42
  * by the proximity heuristic in `compaction-threshold.ts`. See `engine.ts`
43
43
  * top-of-file comment for the full rationale.
44
44
  */
@@ -161,7 +161,7 @@ const clawmemPlugin = {
161
161
  // ----- Plugin Hook: before_prompt_build (AWAITED — load-bearing path) -----
162
162
  // Both context-surfacing retrieval injection and pre-emptive precompact
163
163
  // extraction live here. handleBeforePromptBuild is async and the OpenClaw
164
- // attempt path awaits the result at attempt.ts:2610 before building the
164
+ // attempt path awaits the result at attempt.ts:2973 before building the
165
165
  // effective prompt. precompact-extract therefore runs strictly before
166
166
  // the LLM call that could trigger compaction on this turn.
167
167
  api.on(
@@ -175,7 +175,7 @@ const clawmemPlugin = {
175
175
  // ----- Plugin Hook: agent_end (FIRE-AND-FORGET in core) -----
176
176
  // Decision-extractor, handoff-generator, and feedback-loop run here.
177
177
  // These writes are eventually-consistent (saveMemory dedupes), so the
178
- // fire-and-forget context at attempt.ts:3379-3402 is acceptable.
178
+ // fire-and-forget context at attempt.ts:3870-3892 is acceptable.
179
179
  // OpenClaw v2026.4.26+ also enforces a 30s default void-hook timeout
180
180
  // (DEFAULT_VOID_HOOK_TIMEOUT_MS_BY_HOOK in src/plugins/hooks.ts) — a
181
181
  // timed-out handler is logged but our underlying postrun work is not
package/src/store.ts CHANGED
@@ -298,13 +298,20 @@ if (process.platform === "darwin") {
298
298
  }
299
299
 
300
300
  function initializeDatabase(db: Database): void {
301
+ // Set busy_timeout FIRST so subsequent PRAGMAs (journal_mode in particular,
302
+ // which acquires a write lock when switching or initializing WAL state) wait
303
+ // instead of returning SQLITE_BUSY when concurrent Stop hooks
304
+ // (decision-extractor, handoff-generator, feedback-loop) — and the parallel
305
+ // before_reset hook fan-out in src/openclaw/engine.ts — open the DB
306
+ // simultaneously. busy_timeout is a connection-level setting that only
307
+ // governs *subsequent* statements (default busy handler is NULL → SQLITE_BUSY
308
+ // returns immediately), so it must precede the contending PRAGMAs. 15s is
309
+ // well within the 30s Stop hook timeout. createStore() resets to operational
310
+ // value (5000ms or opts.busyTimeout) after DDL completes. Issue #13.
311
+ db.exec("PRAGMA busy_timeout = 15000");
301
312
  sqliteVec.load(db);
302
313
  db.exec("PRAGMA journal_mode = WAL");
303
314
  db.exec("PRAGMA foreign_keys = ON");
304
- // Set generous busy_timeout during DDL — concurrent Stop hooks (decision-extractor,
305
- // handoff-generator, feedback-loop) all run initializeDatabase simultaneously.
306
- // 15s is well within the 30s Stop hook timeout. Reset to normal after DDL completes.
307
- db.exec("PRAGMA busy_timeout = 15000");
308
315
 
309
316
  // Drop legacy tables that are now managed in YAML
310
317
  db.exec(`DROP TABLE IF EXISTS path_contexts`);
@@ -1123,13 +1130,20 @@ export function createStore(dbPath?: string, opts?: { readonly?: boolean; busyTi
1123
1130
  if (!opts?.readonly) {
1124
1131
  initializeDatabase(db);
1125
1132
  } else {
1126
- // Readonly: load sqlite-vec extension and set WAL mode pragma only
1133
+ // Readonly: set busy_timeout FIRST so the journal_mode PRAGMA below
1134
+ // doesn't race when concurrent processes open the DB. PRAGMA
1135
+ // journal_mode=WAL can contend when switching or initializing WAL
1136
+ // state, even on readonly handles. Public-API hardening — no
1137
+ // production caller in this repo currently passes readonly:true,
1138
+ // but the ordering invariant should hold regardless. Issue #13.
1139
+ db.exec(`PRAGMA busy_timeout = ${opts?.busyTimeout ?? 5000}`);
1127
1140
  sqliteVec.load(db);
1128
1141
  db.exec("PRAGMA journal_mode = WAL");
1129
1142
  db.exec("PRAGMA query_only = ON");
1130
1143
  }
1131
- // Reset busy_timeout to operational value after DDL init (which uses 15s).
1132
- // Default 5000ms for normal operations callers can override via opts.
1144
+ // For the writable branch: initializeDatabase() set 15000 during DDL
1145
+ // reset to operational value here. For readonly: already set inside the
1146
+ // branch above; this assignment is a no-op rewrite to the same value.
1133
1147
  db.exec(`PRAGMA busy_timeout = ${opts?.busyTimeout ?? 5000}`);
1134
1148
 
1135
1149
  return {