memory-braid 0.3.4 → 0.3.7

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/README.md CHANGED
@@ -20,7 +20,7 @@ On the target machine:
20
20
  1. Install from npm:
21
21
 
22
22
  ```bash
23
- openclaw plugins install memory-braid@0.3.4
23
+ openclaw plugins install memory-braid@0.3.5
24
24
  ```
25
25
 
26
26
  2. Rebuild native dependencies inside the installed extension:
@@ -71,6 +71,7 @@ openclaw gateway restart
71
71
  If you install from npm and see native module errors like:
72
72
 
73
73
  - `Could not locate the bindings file` (sqlite3)
74
+ - `.../node_modules/jiti/.../node_sqlite3.node` in the stack/error text
74
75
  - `Cannot find module ... sharp-*.node`
75
76
 
76
77
  run:
@@ -81,6 +82,14 @@ npm rebuild sqlite3 sharp
81
82
  openclaw gateway restart
82
83
  ```
83
84
 
85
+ Note:
86
+ - The `jiti/.../node_sqlite3.node` error is still a sqlite native artifact/runtime loading issue.
87
+ - `memory-braid` now preloads sqlite via native `require` to avoid that path, but you still need `npm rebuild sqlite3 sharp` after `--ignore-scripts` installs.
88
+ - When this happens, startup logs now include `memory_braid.mem0.error` with:
89
+ - `sqliteBindingsError: true`
90
+ - `fixCommand` (copy/paste command for that machine)
91
+ - `pluginDir` (resolved extension directory when available)
92
+
84
93
  ## Quick start: hybrid capture + multilingual NER
85
94
 
86
95
  Add this under `plugins.entries["memory-braid"].config` in your OpenClaw config:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "memory-braid",
3
- "version": "0.3.4",
3
+ "version": "0.3.7",
4
4
  "description": "OpenClaw memory plugin that augments local memory with Mem0, bootstrap import, reconcile, and capture.",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
package/src/index.ts CHANGED
@@ -439,6 +439,34 @@ const memoryBraidPlugin = {
439
439
  return;
440
440
  }
441
441
 
442
+ if (!statePaths) {
443
+ const resolvedStateDir = api.runtime.state.resolveStateDir();
444
+ if (resolvedStateDir) {
445
+ const lazyStatePaths = createStatePaths(resolvedStateDir);
446
+ try {
447
+ await ensureStateDir(lazyStatePaths);
448
+ statePaths = lazyStatePaths;
449
+ mem0.setStateDir(resolvedStateDir);
450
+ entityExtraction.setStateDir(resolvedStateDir);
451
+ log.info("memory_braid.state.ready", {
452
+ runId,
453
+ reason: "lazy_capture",
454
+ stateDir: resolvedStateDir,
455
+ });
456
+ } catch (err) {
457
+ log.warn("memory_braid.capture.skip", {
458
+ runId,
459
+ reason: "state_init_failed",
460
+ workspaceHash: scope.workspaceHash,
461
+ agentId: scope.agentId,
462
+ sessionKey: scope.sessionKey,
463
+ error: err instanceof Error ? err.message : String(err),
464
+ });
465
+ return;
466
+ }
467
+ }
468
+ }
469
+
442
470
  if (!statePaths) {
443
471
  log.warn("memory_braid.capture.skip", {
444
472
  runId,
@@ -1,4 +1,5 @@
1
1
  import fs from "node:fs/promises";
2
+ import { createRequire } from "node:module";
2
3
  import os from "node:os";
3
4
  import path from "node:path";
4
5
  import { normalizeForHash } from "./chunking.js";
@@ -46,6 +47,13 @@ type OssClientLike = {
46
47
 
47
48
  type OssMemoryCtor = new (config?: Record<string, unknown>) => OssClientLike;
48
49
 
50
+ type LoadedOssModule = {
51
+ moduleValue: unknown;
52
+ loader: "require" | "import";
53
+ mem0Path?: string;
54
+ sqlite3Path?: string;
55
+ };
56
+
49
57
  function extractCloudText(memory: CloudRecord): string {
50
58
  const byData = memory.data?.memory;
51
59
  if (typeof byData === "string" && byData.trim()) {
@@ -137,6 +145,48 @@ function resolveCtorFromCandidate(candidate: unknown, depth = 0): OssMemoryCtor
137
145
  );
138
146
  }
139
147
 
148
+ function asErrorMessage(error: unknown): string {
149
+ if (error instanceof Error) {
150
+ return error.message;
151
+ }
152
+ return String(error);
153
+ }
154
+
155
+ function isSqliteBindingsError(error: unknown): boolean {
156
+ const message = asErrorMessage(error);
157
+ return /Could not locate the bindings file/i.test(message) || /node_sqlite3\.node/i.test(message);
158
+ }
159
+
160
+ export function isMem0DeleteNotFoundError(error: unknown): boolean {
161
+ const message = asErrorMessage(error).toLowerCase();
162
+ if (!message) {
163
+ return false;
164
+ }
165
+
166
+ return (
167
+ /\bmemory\b.*\bnot found\b/.test(message) ||
168
+ /\bnot found\b.*\bmemory\b/.test(message) ||
169
+ /\bmemory\b.*\bdoes not exist\b/.test(message) ||
170
+ /\bno such memory\b/.test(message)
171
+ );
172
+ }
173
+
174
+ function createLocalRequire(): NodeJS.Require {
175
+ return createRequire(import.meta.url);
176
+ }
177
+
178
+ function tryResolve(requireFn: NodeJS.Require, id: string): string | undefined {
179
+ try {
180
+ return requireFn.resolve(id);
181
+ } catch {
182
+ return undefined;
183
+ }
184
+ }
185
+
186
+ function tryRequire(requireFn: NodeJS.Require, id: string): unknown {
187
+ return requireFn(id);
188
+ }
189
+
140
190
  export function resolveOssMemoryCtor(moduleValue: unknown): OssMemoryCtor | undefined {
141
191
  return (
142
192
  resolveCtorFromCandidate(moduleValue) ??
@@ -300,11 +350,13 @@ export class Mem0Adapter {
300
350
  private ossClient: OssClientLike | null = null;
301
351
  private readonly cfg: MemoryBraidConfig;
302
352
  private readonly log: MemoryBraidLogger;
353
+ private readonly pluginDir?: string;
303
354
  private stateDir?: string;
304
355
 
305
356
  constructor(cfg: MemoryBraidConfig, log: MemoryBraidLogger, options?: Mem0AdapterOptions) {
306
357
  this.cfg = cfg;
307
358
  this.log = log;
359
+ this.pluginDir = this.resolvePluginDir();
308
360
  this.stateDir = options?.stateDir;
309
361
  }
310
362
 
@@ -362,7 +414,7 @@ export class Mem0Adapter {
362
414
  }
363
415
 
364
416
  try {
365
- const mod = await import("mem0ai/oss");
417
+ const { moduleValue: mod, loader, mem0Path, sqlite3Path } = await this.loadOssModule();
366
418
  const Memory = resolveOssMemoryCtor(mod);
367
419
  if (!Memory) {
368
420
  const exportKeys = Object.keys(asRecord(mod));
@@ -384,20 +436,104 @@ export class Mem0Adapter {
384
436
  this.log.debug("memory_braid.mem0.response", {
385
437
  action: "init",
386
438
  mode: "oss",
439
+ loader,
440
+ mem0Path,
441
+ sqlite3Path,
387
442
  hasCustomConfig,
388
443
  sqliteDbPaths: collectSqliteDbPaths(configToUse),
389
444
  }, true);
390
445
  return this.ossClient;
391
446
  } catch (err) {
447
+ const sqliteBindingsError = isSqliteBindingsError(err);
392
448
  this.log.error("memory_braid.mem0.error", {
393
449
  reason: "init_failed",
394
450
  mode: "oss",
451
+ sqliteBindingsError,
452
+ ...(sqliteBindingsError ? this.nativeRebuildHint() : {}),
395
453
  error: err instanceof Error ? err.message : String(err),
396
454
  });
397
455
  return null;
398
456
  }
399
457
  }
400
458
 
459
+ private async loadOssModule(): Promise<LoadedOssModule> {
460
+ const requireFromHere = createLocalRequire();
461
+ const sqlite3Path = tryResolve(requireFromHere, "sqlite3");
462
+ if (sqlite3Path) {
463
+ try {
464
+ tryRequire(requireFromHere, sqlite3Path);
465
+ } catch (error) {
466
+ this.log.warn("memory_braid.mem0.error", {
467
+ reason: "sqlite3_preload_failed",
468
+ mode: "oss",
469
+ ...this.nativeRebuildHint(),
470
+ sqlite3Path,
471
+ error: asErrorMessage(error),
472
+ });
473
+ }
474
+ }
475
+
476
+ const mem0Path = tryResolve(requireFromHere, "mem0ai/oss");
477
+ if (mem0Path) {
478
+ try {
479
+ const required = tryRequire(requireFromHere, mem0Path);
480
+ return {
481
+ moduleValue: required,
482
+ loader: "require",
483
+ mem0Path,
484
+ sqlite3Path,
485
+ };
486
+ } catch (error) {
487
+ const sqliteBindingsError = isSqliteBindingsError(error);
488
+ this.log.warn("memory_braid.mem0.error", {
489
+ reason: "oss_require_failed",
490
+ mode: "oss",
491
+ mem0Path,
492
+ sqlite3Path,
493
+ sqliteBindingsError,
494
+ ...(sqliteBindingsError ? this.nativeRebuildHint() : {}),
495
+ error: asErrorMessage(error),
496
+ });
497
+ }
498
+ }
499
+
500
+ const imported = await import("mem0ai/oss");
501
+ return {
502
+ moduleValue: imported,
503
+ loader: "import",
504
+ mem0Path,
505
+ sqlite3Path,
506
+ };
507
+ }
508
+
509
+ private resolvePluginDir(): string | undefined {
510
+ const requireFromHere = createLocalRequire();
511
+ const packageJsonPath = tryResolve(requireFromHere, "../package.json");
512
+ if (!packageJsonPath) {
513
+ return undefined;
514
+ }
515
+ return path.dirname(packageJsonPath);
516
+ }
517
+
518
+ private nativeRebuildHint(): {
519
+ pluginDir?: string;
520
+ fixCommand: string;
521
+ why: string;
522
+ } {
523
+ if (this.pluginDir) {
524
+ return {
525
+ pluginDir: this.pluginDir,
526
+ fixCommand: `cd "${this.pluginDir}" && npm rebuild sqlite3 sharp && openclaw gateway restart`,
527
+ why: "OpenClaw plugin installs use --ignore-scripts, so sqlite3/sharp native artifacts may be missing after install/update.",
528
+ };
529
+ }
530
+
531
+ return {
532
+ fixCommand: "cd ~/.openclaw/extensions/memory-braid && npm rebuild sqlite3 sharp && openclaw gateway restart",
533
+ why: "OpenClaw plugin installs use --ignore-scripts, so sqlite3/sharp native artifacts may be missing after install/update.",
534
+ };
535
+ }
536
+
401
537
  async ensureClient(): Promise<{ mode: "cloud" | "oss"; client: CloudClientLike | OssClientLike } | null> {
402
538
  if (this.cfg.mem0.mode === "oss") {
403
539
  const client = await this.ensureOssClient();
@@ -616,6 +752,21 @@ export class Mem0Adapter {
616
752
  });
617
753
  return true;
618
754
  } catch (err) {
755
+ const missingRemote = isMem0DeleteNotFoundError(err);
756
+ if (missingRemote) {
757
+ this.log.debug("memory_braid.mem0.response", {
758
+ runId: params.runId,
759
+ action: "delete",
760
+ mode: prepared.mode,
761
+ workspaceHash: params.scope.workspaceHash,
762
+ agentId: params.scope.agentId,
763
+ memoryId: params.memoryId,
764
+ durMs: Date.now() - startedAt,
765
+ alreadyMissing: true,
766
+ });
767
+ return true;
768
+ }
769
+
619
770
  this.log.warn("memory_braid.mem0.error", {
620
771
  runId: params.runId,
621
772
  action: "delete",