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 +10 -1
- package/package.json +1 -1
- package/src/index.ts +28 -0
- package/src/mem0-client.ts +152 -1
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.
|
|
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
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,
|
package/src/mem0-client.ts
CHANGED
|
@@ -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
|
|
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",
|