clawmem 0.15.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.
@@ -12,6 +12,15 @@ Common issues when running ClawMem with hooks, MCP server, or OpenClaw plugin. O
12
12
  - If both snap bun (`/snap/bin/bun`) and native bun (`~/.bun/bin/bun`) are installed, `which bun` may return the snap version. Direct `bun -e` or `bun run` commands will use the wrong binary.
13
13
  - Fix: The `bin/clawmem` wrapper handles this automatically. For manual commands, use `~/.bun/bin/bun` explicitly or add `~/.bun/bin` to PATH before `/snap/bin`.
14
14
 
15
+ **macOS: `bootstrap` / `doctor` fails with "does not support dynamic extension loading" (sqlite-vec on macOS)**
16
+ - `clawmem bootstrap` (or `clawmem doctor`) fails at the database step because macOS's built-in SQLite — which Bun uses by default — is compiled without extension-loading support, so the `sqlite-vec` vector extension cannot load. Symptom: `✗ Database: ... This build of sqlite3 does not support dynamic extension loading`. Yoloshii/ClawMem#20.
17
+ - Fix: install an extension-capable SQLite via Homebrew, then re-run bootstrap:
18
+ ```bash
19
+ brew install sqlite
20
+ clawmem bootstrap ~/notes --name notes
21
+ ```
22
+ - ClawMem auto-detects Homebrew's SQLite at the standard prefixes (`/opt/homebrew` on Apple Silicon, `/usr/local` on Intel) and at `brew --prefix sqlite` for non-standard prefixes — installing it is all that's required, no env var or config. If it still fails after `brew install sqlite`, run `brew reinstall sqlite` and confirm `ls $(brew --prefix sqlite)/lib/libsqlite3.dylib` resolves.
23
+
15
24
  ## Embedding & GPU
16
25
 
17
26
  **"Local model download blocked" error**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clawmem",
3
- "version": "0.15.0",
3
+ "version": "0.15.1",
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": {
package/src/store.ts CHANGED
@@ -14,7 +14,7 @@
14
14
 
15
15
  import { Database } from "bun:sqlite";
16
16
  import { Glob } from "bun";
17
- import { realpathSync } from "node:fs";
17
+ import { realpathSync, existsSync } from "node:fs";
18
18
  import * as sqliteVec from "sqlite-vec";
19
19
  import {
20
20
  LlamaCpp,
@@ -290,14 +290,79 @@ export function toVirtualPath(db: Database, absolutePath: string): string | null
290
290
  // Database initialization
291
291
  // =============================================================================
292
292
 
293
- // On macOS, use Homebrew's SQLite which supports extensions
293
+ // On macOS, Apple's built-in libsqlite3 — which Bun uses by default — is
294
+ // compiled WITHOUT extension-loading support, so sqliteVec.load() fails with
295
+ // "This build of sqlite3 does not support dynamic extension loading" (Issue #20).
296
+ // Point Bun at an extension-capable SQLite (Homebrew's) via setCustomSQLite()
297
+ // BEFORE the first Database is opened. setCustomSQLite() must receive a path
298
+ // that EXISTS — an invalid path hard-crashes Bun (oven-sh/bun#18811) — so every
299
+ // candidate is existence-checked first. The resolved path (or null) is recorded
300
+ // so loadVecExtension() can emit an actionable error when no extension-capable
301
+ // SQLite is installed, instead of the cryptic extension-loading failure.
302
+
303
+ /** macOS only: the extension-capable SQLite activated via setCustomSQLite, or null if none was found. */
304
+ let macosCustomSqlitePath: string | null = null;
305
+
294
306
  if (process.platform === "darwin") {
295
- const homebrewSqlitePath = "/opt/homebrew/opt/sqlite/lib/libsqlite3.dylib";
307
+ const candidates = [
308
+ "/opt/homebrew/opt/sqlite/lib/libsqlite3.dylib", // Homebrew (Apple Silicon)
309
+ "/usr/local/opt/sqlite/lib/libsqlite3.dylib", // Homebrew (Intel)
310
+ ];
311
+ // For a non-standard Homebrew prefix, ask brew directly — but only when the
312
+ // standard paths are absent, so the common case pays no subprocess cost.
313
+ if (!candidates.some(p => existsSync(p))) {
314
+ try {
315
+ const brew = Bun.spawnSync(["brew", "--prefix", "sqlite"], { stdout: "pipe", stderr: "ignore" });
316
+ const prefix = brew.success ? brew.stdout.toString().trim() : "";
317
+ if (prefix) candidates.push(`${prefix}/lib/libsqlite3.dylib`);
318
+ } catch { /* brew not installed — nothing more to probe */ }
319
+ }
320
+ for (const candidate of candidates) {
321
+ if (!existsSync(candidate)) continue;
322
+ try {
323
+ Database.setCustomSQLite(candidate);
324
+ macosCustomSqlitePath = candidate;
325
+ break;
326
+ } catch { /* not usable — try the next candidate */ }
327
+ }
328
+ }
329
+
330
+ /**
331
+ * Translate a sqlite-vec load failure into an actionable error on macOS, where
332
+ * the default system SQLite cannot load extensions (Issue #20). Pure + exported
333
+ * for tests: pass `platform` / `foundPath` explicitly to exercise either branch.
334
+ * Non-macOS, or any unrelated error, is returned unchanged.
335
+ */
336
+ export function explainVecLoadError(
337
+ err: unknown,
338
+ platform: string = process.platform,
339
+ foundPath: string | null = macosCustomSqlitePath,
340
+ ): Error {
341
+ const original = err instanceof Error ? err : new Error(String(err));
342
+ const isExtensionError = /does not support dynamic extension loading/i.test(original.message);
343
+ if (platform !== "darwin" || !isExtensionError) return original;
344
+
345
+ const detail = foundPath === null
346
+ ? "No Homebrew SQLite was found at the standard locations (/opt/homebrew or /usr/local)."
347
+ : `A custom SQLite was set from ${foundPath}, but it still cannot load extensions — try 'brew reinstall sqlite'.`;
348
+ return new Error(
349
+ "ClawMem could not load the sqlite-vec extension: macOS's built-in SQLite is " +
350
+ "compiled without extension support.\n" +
351
+ "Fix: install an extension-capable SQLite with Homebrew, then re-run:\n" +
352
+ " brew install sqlite\n" +
353
+ `${detail}\n` +
354
+ "More detail: docs/troubleshooting.md (\"Bun runtime\" -> sqlite-vec on macOS), Yoloshii/ClawMem#20.\n" +
355
+ `Original error: ${original.message}`,
356
+ );
357
+ }
358
+
359
+ /** Load the sqlite-vec extension, surfacing an actionable error on macOS (Issue #20). */
360
+ function loadVecExtension(db: Database): void {
296
361
  try {
297
- if (Bun.file(homebrewSqlitePath).size > 0) {
298
- Database.setCustomSQLite(homebrewSqlitePath);
299
- }
300
- } catch { }
362
+ sqliteVec.load(db);
363
+ } catch (err) {
364
+ throw explainVecLoadError(err);
365
+ }
301
366
  }
302
367
 
303
368
  function initializeDatabase(db: Database): void {
@@ -312,7 +377,7 @@ function initializeDatabase(db: Database): void {
312
377
  // well within the 30s Stop hook timeout. createStore() resets to operational
313
378
  // value (5000ms or opts.busyTimeout) after DDL completes. Issue #13.
314
379
  db.exec("PRAGMA busy_timeout = 15000");
315
- sqliteVec.load(db);
380
+ loadVecExtension(db);
316
381
  db.exec("PRAGMA journal_mode = WAL");
317
382
  db.exec("PRAGMA foreign_keys = ON");
318
383
 
@@ -1283,7 +1348,7 @@ export function createStore(dbPath?: string, opts?: { readonly?: boolean; busyTi
1283
1348
  // production caller in this repo currently passes readonly:true,
1284
1349
  // but the ordering invariant should hold regardless. Issue #13.
1285
1350
  db.exec(`PRAGMA busy_timeout = ${opts?.busyTimeout ?? 5000}`);
1286
- sqliteVec.load(db);
1351
+ loadVecExtension(db);
1287
1352
  db.exec("PRAGMA journal_mode = WAL");
1288
1353
  db.exec("PRAGMA query_only = ON");
1289
1354
  }