memory-braid 0.3.3 → 0.3.6
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/mem0-client.ts +150 -10
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/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()) {
|
|
@@ -111,20 +119,66 @@ function asOssMemoryCtor(value: unknown): OssMemoryCtor | undefined {
|
|
|
111
119
|
return value as OssMemoryCtor;
|
|
112
120
|
}
|
|
113
121
|
|
|
114
|
-
|
|
115
|
-
|
|
122
|
+
function isObjectLike(value: unknown): value is Record<string, unknown> {
|
|
123
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function resolveCtorFromCandidate(candidate: unknown, depth = 0): OssMemoryCtor | undefined {
|
|
127
|
+
if (depth > 6 || !candidate) {
|
|
116
128
|
return undefined;
|
|
117
129
|
}
|
|
118
130
|
|
|
119
|
-
const
|
|
120
|
-
|
|
131
|
+
const direct = asOssMemoryCtor(candidate);
|
|
132
|
+
if (direct) {
|
|
133
|
+
return direct;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (!isObjectLike(candidate)) {
|
|
137
|
+
return undefined;
|
|
138
|
+
}
|
|
121
139
|
|
|
140
|
+
const record = candidate as Record<string, unknown>;
|
|
122
141
|
return (
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
142
|
+
resolveCtorFromCandidate(record.Memory, depth + 1) ??
|
|
143
|
+
resolveCtorFromCandidate(record.MemoryClient, depth + 1) ??
|
|
144
|
+
resolveCtorFromCandidate(record.default, depth + 1)
|
|
145
|
+
);
|
|
146
|
+
}
|
|
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
|
+
function createLocalRequire(): NodeJS.Require {
|
|
161
|
+
return createRequire(import.meta.url);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function tryResolve(requireFn: NodeJS.Require, id: string): string | undefined {
|
|
165
|
+
try {
|
|
166
|
+
return requireFn.resolve(id);
|
|
167
|
+
} catch {
|
|
168
|
+
return undefined;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function tryRequire(requireFn: NodeJS.Require, id: string): unknown {
|
|
173
|
+
return requireFn(id);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export function resolveOssMemoryCtor(moduleValue: unknown): OssMemoryCtor | undefined {
|
|
177
|
+
return (
|
|
178
|
+
resolveCtorFromCandidate(moduleValue) ??
|
|
179
|
+
resolveCtorFromCandidate(asRecord(moduleValue).Memory) ??
|
|
180
|
+
resolveCtorFromCandidate(asRecord(moduleValue).MemoryClient) ??
|
|
181
|
+
resolveCtorFromCandidate(asRecord(moduleValue).default)
|
|
128
182
|
);
|
|
129
183
|
}
|
|
130
184
|
|
|
@@ -282,11 +336,13 @@ export class Mem0Adapter {
|
|
|
282
336
|
private ossClient: OssClientLike | null = null;
|
|
283
337
|
private readonly cfg: MemoryBraidConfig;
|
|
284
338
|
private readonly log: MemoryBraidLogger;
|
|
339
|
+
private readonly pluginDir?: string;
|
|
285
340
|
private stateDir?: string;
|
|
286
341
|
|
|
287
342
|
constructor(cfg: MemoryBraidConfig, log: MemoryBraidLogger, options?: Mem0AdapterOptions) {
|
|
288
343
|
this.cfg = cfg;
|
|
289
344
|
this.log = log;
|
|
345
|
+
this.pluginDir = this.resolvePluginDir();
|
|
290
346
|
this.stateDir = options?.stateDir;
|
|
291
347
|
}
|
|
292
348
|
|
|
@@ -344,7 +400,7 @@ export class Mem0Adapter {
|
|
|
344
400
|
}
|
|
345
401
|
|
|
346
402
|
try {
|
|
347
|
-
const mod = await
|
|
403
|
+
const { moduleValue: mod, loader, mem0Path, sqlite3Path } = await this.loadOssModule();
|
|
348
404
|
const Memory = resolveOssMemoryCtor(mod);
|
|
349
405
|
if (!Memory) {
|
|
350
406
|
const exportKeys = Object.keys(asRecord(mod));
|
|
@@ -366,20 +422,104 @@ export class Mem0Adapter {
|
|
|
366
422
|
this.log.debug("memory_braid.mem0.response", {
|
|
367
423
|
action: "init",
|
|
368
424
|
mode: "oss",
|
|
425
|
+
loader,
|
|
426
|
+
mem0Path,
|
|
427
|
+
sqlite3Path,
|
|
369
428
|
hasCustomConfig,
|
|
370
429
|
sqliteDbPaths: collectSqliteDbPaths(configToUse),
|
|
371
430
|
}, true);
|
|
372
431
|
return this.ossClient;
|
|
373
432
|
} catch (err) {
|
|
433
|
+
const sqliteBindingsError = isSqliteBindingsError(err);
|
|
374
434
|
this.log.error("memory_braid.mem0.error", {
|
|
375
435
|
reason: "init_failed",
|
|
376
436
|
mode: "oss",
|
|
437
|
+
sqliteBindingsError,
|
|
438
|
+
...(sqliteBindingsError ? this.nativeRebuildHint() : {}),
|
|
377
439
|
error: err instanceof Error ? err.message : String(err),
|
|
378
440
|
});
|
|
379
441
|
return null;
|
|
380
442
|
}
|
|
381
443
|
}
|
|
382
444
|
|
|
445
|
+
private async loadOssModule(): Promise<LoadedOssModule> {
|
|
446
|
+
const requireFromHere = createLocalRequire();
|
|
447
|
+
const sqlite3Path = tryResolve(requireFromHere, "sqlite3");
|
|
448
|
+
if (sqlite3Path) {
|
|
449
|
+
try {
|
|
450
|
+
tryRequire(requireFromHere, sqlite3Path);
|
|
451
|
+
} catch (error) {
|
|
452
|
+
this.log.warn("memory_braid.mem0.error", {
|
|
453
|
+
reason: "sqlite3_preload_failed",
|
|
454
|
+
mode: "oss",
|
|
455
|
+
...this.nativeRebuildHint(),
|
|
456
|
+
sqlite3Path,
|
|
457
|
+
error: asErrorMessage(error),
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
const mem0Path = tryResolve(requireFromHere, "mem0ai/oss");
|
|
463
|
+
if (mem0Path) {
|
|
464
|
+
try {
|
|
465
|
+
const required = tryRequire(requireFromHere, mem0Path);
|
|
466
|
+
return {
|
|
467
|
+
moduleValue: required,
|
|
468
|
+
loader: "require",
|
|
469
|
+
mem0Path,
|
|
470
|
+
sqlite3Path,
|
|
471
|
+
};
|
|
472
|
+
} catch (error) {
|
|
473
|
+
const sqliteBindingsError = isSqliteBindingsError(error);
|
|
474
|
+
this.log.warn("memory_braid.mem0.error", {
|
|
475
|
+
reason: "oss_require_failed",
|
|
476
|
+
mode: "oss",
|
|
477
|
+
mem0Path,
|
|
478
|
+
sqlite3Path,
|
|
479
|
+
sqliteBindingsError,
|
|
480
|
+
...(sqliteBindingsError ? this.nativeRebuildHint() : {}),
|
|
481
|
+
error: asErrorMessage(error),
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
const imported = await import("mem0ai/oss");
|
|
487
|
+
return {
|
|
488
|
+
moduleValue: imported,
|
|
489
|
+
loader: "import",
|
|
490
|
+
mem0Path,
|
|
491
|
+
sqlite3Path,
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
private resolvePluginDir(): string | undefined {
|
|
496
|
+
const requireFromHere = createLocalRequire();
|
|
497
|
+
const packageJsonPath = tryResolve(requireFromHere, "../package.json");
|
|
498
|
+
if (!packageJsonPath) {
|
|
499
|
+
return undefined;
|
|
500
|
+
}
|
|
501
|
+
return path.dirname(packageJsonPath);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
private nativeRebuildHint(): {
|
|
505
|
+
pluginDir?: string;
|
|
506
|
+
fixCommand: string;
|
|
507
|
+
why: string;
|
|
508
|
+
} {
|
|
509
|
+
if (this.pluginDir) {
|
|
510
|
+
return {
|
|
511
|
+
pluginDir: this.pluginDir,
|
|
512
|
+
fixCommand: `cd "${this.pluginDir}" && npm rebuild sqlite3 sharp && openclaw gateway restart`,
|
|
513
|
+
why: "OpenClaw plugin installs use --ignore-scripts, so sqlite3/sharp native artifacts may be missing after install/update.",
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
return {
|
|
518
|
+
fixCommand: "cd ~/.openclaw/extensions/memory-braid && npm rebuild sqlite3 sharp && openclaw gateway restart",
|
|
519
|
+
why: "OpenClaw plugin installs use --ignore-scripts, so sqlite3/sharp native artifacts may be missing after install/update.",
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
|
|
383
523
|
async ensureClient(): Promise<{ mode: "cloud" | "oss"; client: CloudClientLike | OssClientLike } | null> {
|
|
384
524
|
if (this.cfg.mem0.mode === "oss") {
|
|
385
525
|
const client = await this.ensureOssClient();
|