@xdarkicex/openclaw-memory-libravdb 1.10.3 → 1.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/README.md +4 -0
- package/dist/identity.d.ts +1 -0
- package/dist/identity.js +18 -3
- package/dist/index.js +50 -6
- package/dist/libravdb-client.d.ts +4 -0
- package/dist/libravdb-client.js +29 -1
- package/dist/rules.d.ts +1 -0
- package/dist/rules.js +5 -2
- package/dist/types.d.ts +9 -3
- package/openclaw.plugin.json +7 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -205,6 +205,8 @@ Keywords: "acme corp, acmecorp, acme.com"
|
|
|
205
205
|
If the agent's reply contains any of those keywords, it's blocked and replaced
|
|
206
206
|
with "I cannot answer that." — regardless of what the model chose to say.
|
|
207
207
|
|
|
208
|
+
**Config:** `maxRules` sets the cap (default 20, set to 0 to disable rules entirely).
|
|
209
|
+
|
|
208
210
|
**Storage:** persisted to `~/.openclaw/cache/libravdb/rules.json`. Survives
|
|
209
211
|
gateway restarts and plugin updates.
|
|
210
212
|
|
|
@@ -307,6 +309,8 @@ All keys are optional. For the full reference, see [Configuration](./docs/config
|
|
|
307
309
|
| `onnxDevice` | string | `cpu` | ONNX execution provider; `cpu` is the default; `auto` lets libravdbd auto-detect |
|
|
308
310
|
| `userId` | string | auto-derived | Stable identity for cross-session durable memory |
|
|
309
311
|
| `tenantId` | string | auto-derived | Multi-tenant identifier. Resolved as `cfg.tenantId` > `LIBRAVDB_AGENT_ID` env > `userId`. Isolates the agent to a dedicated `.libravdb` file. |
|
|
312
|
+
| `tenantIdByAgent` | object | — | Per-agent tenant map. `{"agent1": "t1"}`. Unlisted agents fall through to `tenantId` → `userId`. |
|
|
313
|
+
| `maxRules` | number | 20 | Max hard constraint rules. Set to 0 to disable rules entirely. |
|
|
310
314
|
| `crossSessionRecall` | boolean | `true` | When `false`, only session-scoped memories are retrieved |
|
|
311
315
|
| `compactSessionTokenBudget` | number | `2000` | Auto-compaction token threshold; `0` disables |
|
|
312
316
|
|
package/dist/identity.d.ts
CHANGED
|
@@ -22,3 +22,4 @@ export declare function resolveIdentity(params: {
|
|
|
22
22
|
* 3. Fall back to resolved userId (existing identity system)
|
|
23
23
|
*/
|
|
24
24
|
export declare function resolveTenantKey(cfg: PluginConfig, agentId?: string): string;
|
|
25
|
+
export declare function resolveReadTenants(cfg: PluginConfig, agentId?: string): string[] | null;
|
package/dist/identity.js
CHANGED
|
@@ -129,9 +129,11 @@ export function resolveIdentity(params) {
|
|
|
129
129
|
export function resolveTenantKey(cfg, agentId) {
|
|
130
130
|
// Per-agent override (highest priority).
|
|
131
131
|
if (agentId && cfg.tenantIdByAgent) {
|
|
132
|
-
const
|
|
133
|
-
if (
|
|
134
|
-
return
|
|
132
|
+
const entry = cfg.tenantIdByAgent[agentId];
|
|
133
|
+
if (typeof entry === "string")
|
|
134
|
+
return entry.trim();
|
|
135
|
+
if (entry && typeof entry === "object")
|
|
136
|
+
return entry.primary.trim();
|
|
135
137
|
}
|
|
136
138
|
const explicit = cfg.tenantId?.trim();
|
|
137
139
|
if (explicit)
|
|
@@ -144,3 +146,16 @@ export function resolveTenantKey(cfg, agentId) {
|
|
|
144
146
|
identityPath: cfg.identityPath,
|
|
145
147
|
}).userId;
|
|
146
148
|
}
|
|
149
|
+
// resolveReadTenants returns all tenant keys an agent can read from
|
|
150
|
+
// (primary + readAccess). Empty if single-tenant. Used for search fan-out.
|
|
151
|
+
export function resolveReadTenants(cfg, agentId) {
|
|
152
|
+
if (!agentId || !cfg.tenantIdByAgent)
|
|
153
|
+
return null;
|
|
154
|
+
const entry = cfg.tenantIdByAgent[agentId];
|
|
155
|
+
if (!entry || typeof entry === "string")
|
|
156
|
+
return null; // string = primary only, no fan-out
|
|
157
|
+
if (entry.readAccess && entry.readAccess.length > 0) {
|
|
158
|
+
return [entry.primary.trim(), ...entry.readAccess.map((t) => t.trim())];
|
|
159
|
+
}
|
|
160
|
+
return null;
|
|
161
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -16902,8 +16902,9 @@ function resolveIdentity(params) {
|
|
|
16902
16902
|
}
|
|
16903
16903
|
function resolveTenantKey(cfg, agentId) {
|
|
16904
16904
|
if (agentId && cfg.tenantIdByAgent) {
|
|
16905
|
-
const
|
|
16906
|
-
if (
|
|
16905
|
+
const entry = cfg.tenantIdByAgent[agentId];
|
|
16906
|
+
if (typeof entry === "string") return entry.trim();
|
|
16907
|
+
if (entry && typeof entry === "object") return entry.primary.trim();
|
|
16907
16908
|
}
|
|
16908
16909
|
const explicit = cfg.tenantId?.trim();
|
|
16909
16910
|
if (explicit) return explicit;
|
|
@@ -16914,6 +16915,15 @@ function resolveTenantKey(cfg, agentId) {
|
|
|
16914
16915
|
identityPath: cfg.identityPath
|
|
16915
16916
|
}).userId;
|
|
16916
16917
|
}
|
|
16918
|
+
function resolveReadTenants(cfg, agentId) {
|
|
16919
|
+
if (!agentId || !cfg.tenantIdByAgent) return null;
|
|
16920
|
+
const entry = cfg.tenantIdByAgent[agentId];
|
|
16921
|
+
if (!entry || typeof entry === "string") return null;
|
|
16922
|
+
if (entry.readAccess && entry.readAccess.length > 0) {
|
|
16923
|
+
return [entry.primary.trim(), ...entry.readAccess.map((t) => t.trim())];
|
|
16924
|
+
}
|
|
16925
|
+
return null;
|
|
16926
|
+
}
|
|
16917
16927
|
|
|
16918
16928
|
// src/index.ts
|
|
16919
16929
|
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
|
|
@@ -18436,7 +18446,10 @@ import { join as join3 } from "node:path";
|
|
|
18436
18446
|
// src/rules.ts
|
|
18437
18447
|
import { mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
18438
18448
|
import { dirname as dirname2 } from "node:path";
|
|
18439
|
-
var
|
|
18449
|
+
var maxRules = 20;
|
|
18450
|
+
function setMaxRules(n) {
|
|
18451
|
+
maxRules = Math.max(0, n);
|
|
18452
|
+
}
|
|
18440
18453
|
function jsonResult(details) {
|
|
18441
18454
|
return { content: [{ type: "text", text: JSON.stringify(details, null, 2) }], details };
|
|
18442
18455
|
}
|
|
@@ -18475,7 +18488,7 @@ function getRule(id) {
|
|
|
18475
18488
|
}
|
|
18476
18489
|
function setRule(ruleText, keywords, priority) {
|
|
18477
18490
|
let replaced = false;
|
|
18478
|
-
if (rules.length >=
|
|
18491
|
+
if (maxRules > 0 && rules.length >= maxRules) {
|
|
18479
18492
|
let minIdx = 0;
|
|
18480
18493
|
for (let i = 1; i < rules.length; i++) {
|
|
18481
18494
|
if (rules[i].priority < rules[minIdx].priority) minIdx = i;
|
|
@@ -35010,6 +35023,7 @@ var LibravDBClient = class {
|
|
|
35010
35023
|
nonceHex;
|
|
35011
35024
|
closed = false;
|
|
35012
35025
|
tenantKey;
|
|
35026
|
+
readTenants = [];
|
|
35013
35027
|
constructor(options = {}) {
|
|
35014
35028
|
this.secret = options.secret ?? loadSecretFromEnv();
|
|
35015
35029
|
const rawEndpoint = resolveClientEndpoint(options.endpoint);
|
|
@@ -35100,6 +35114,11 @@ var LibravDBClient = class {
|
|
|
35100
35114
|
setTenantKey(key) {
|
|
35101
35115
|
this.tenantKey = key;
|
|
35102
35116
|
}
|
|
35117
|
+
/** Set additional tenants to search across. Only used by searchTextCollections.
|
|
35118
|
+
* Empty by default — zero overhead for single-tenant setups. */
|
|
35119
|
+
setReadTenants(tenants) {
|
|
35120
|
+
this.readTenants = tenants;
|
|
35121
|
+
}
|
|
35103
35122
|
// ── Session lifecycle ────────────────────────────────────────────
|
|
35104
35123
|
async health(req = {}) {
|
|
35105
35124
|
this.guardOpen();
|
|
@@ -35149,7 +35168,27 @@ var LibravDBClient = class {
|
|
|
35149
35168
|
}
|
|
35150
35169
|
async searchTextCollections(req) {
|
|
35151
35170
|
this.guardOpen();
|
|
35152
|
-
|
|
35171
|
+
if (this.readTenants.length === 0) {
|
|
35172
|
+
return this.client.searchTextCollections(req);
|
|
35173
|
+
}
|
|
35174
|
+
const savedKey = this.tenantKey;
|
|
35175
|
+
const allResults = [];
|
|
35176
|
+
const seen = /* @__PURE__ */ new Set();
|
|
35177
|
+
for (const t of this.readTenants) {
|
|
35178
|
+
this.tenantKey = t;
|
|
35179
|
+
try {
|
|
35180
|
+
const resp = await this.client.searchTextCollections(req);
|
|
35181
|
+
for (const r of resp.results ?? []) {
|
|
35182
|
+
if (!seen.has(r.id)) {
|
|
35183
|
+
seen.add(r.id);
|
|
35184
|
+
allResults.push(r);
|
|
35185
|
+
}
|
|
35186
|
+
}
|
|
35187
|
+
} catch {
|
|
35188
|
+
}
|
|
35189
|
+
}
|
|
35190
|
+
this.tenantKey = savedKey;
|
|
35191
|
+
return { results: allResults };
|
|
35153
35192
|
}
|
|
35154
35193
|
async listCollection(req) {
|
|
35155
35194
|
this.guardOpen();
|
|
@@ -35516,7 +35555,10 @@ function register(api) {
|
|
|
35516
35555
|
const runtimeOrNull = isLightweight ? null : createPluginRuntime(cfg, logger);
|
|
35517
35556
|
if (runtimeOrNull && !isLightweight) {
|
|
35518
35557
|
const cacheDir = api.cacheDir ?? process.env.OPENCLAW_CACHE_DIR ?? (process.env.HOME || process.env.USERPROFILE || "") + "/.openclaw/cache";
|
|
35519
|
-
if (cacheDir)
|
|
35558
|
+
if (cacheDir) {
|
|
35559
|
+
initRuleStore(cacheDir + "/libravdb", logger);
|
|
35560
|
+
if (cfg.maxRules !== void 0) setMaxRules(cfg.maxRules);
|
|
35561
|
+
}
|
|
35520
35562
|
}
|
|
35521
35563
|
registerMemoryCli(api, runtimeOrNull, cfg, logger);
|
|
35522
35564
|
const ownsMemorySlot = memSlot === MEMORY_ID;
|
|
@@ -35667,9 +35709,11 @@ function register(api) {
|
|
|
35667
35709
|
if (runtimeOrNull) {
|
|
35668
35710
|
const agentId = c?.agentId;
|
|
35669
35711
|
const tenantKey = resolveTenantKey(cfg, agentId);
|
|
35712
|
+
const readTenants = resolveReadTenants(cfg, agentId);
|
|
35670
35713
|
try {
|
|
35671
35714
|
const client = await runtimeOrNull.getClient();
|
|
35672
35715
|
client.setTenantKey(tenantKey);
|
|
35716
|
+
client.setReadTenants(readTenants ?? []);
|
|
35673
35717
|
} catch {
|
|
35674
35718
|
}
|
|
35675
35719
|
}
|
|
@@ -36,12 +36,16 @@ export declare class LibravDBClient {
|
|
|
36
36
|
private nonceHex;
|
|
37
37
|
private closed;
|
|
38
38
|
private tenantKey;
|
|
39
|
+
private readTenants;
|
|
39
40
|
constructor(options?: LibravDBClientOptions);
|
|
40
41
|
bootstrapHandshake(): Promise<void>;
|
|
41
42
|
private guardOpen;
|
|
42
43
|
/** Update the tenant key for subsequent gRPC calls. Thread-safe —
|
|
43
44
|
* the interceptor reads this.tenantKey on every request. */
|
|
44
45
|
setTenantKey(key: string): void;
|
|
46
|
+
/** Set additional tenants to search across. Only used by searchTextCollections.
|
|
47
|
+
* Empty by default — zero overhead for single-tenant setups. */
|
|
48
|
+
setReadTenants(tenants: string[]): void;
|
|
45
49
|
health(req?: PartialMessage<HealthRequest>): Promise<HealthResponse>;
|
|
46
50
|
status(req?: PartialMessage<MemoryStatusRequest>): Promise<MemoryStatusResponse>;
|
|
47
51
|
flush(req?: PartialMessage<FlushRequest>): Promise<FlushResponse>;
|
package/dist/libravdb-client.js
CHANGED
|
@@ -150,6 +150,7 @@ export class LibravDBClient {
|
|
|
150
150
|
nonceHex;
|
|
151
151
|
closed = false;
|
|
152
152
|
tenantKey;
|
|
153
|
+
readTenants = [];
|
|
153
154
|
constructor(options = {}) {
|
|
154
155
|
this.secret = options.secret ?? loadSecretFromEnv();
|
|
155
156
|
const rawEndpoint = resolveClientEndpoint(options.endpoint);
|
|
@@ -237,6 +238,11 @@ export class LibravDBClient {
|
|
|
237
238
|
setTenantKey(key) {
|
|
238
239
|
this.tenantKey = key;
|
|
239
240
|
}
|
|
241
|
+
/** Set additional tenants to search across. Only used by searchTextCollections.
|
|
242
|
+
* Empty by default — zero overhead for single-tenant setups. */
|
|
243
|
+
setReadTenants(tenants) {
|
|
244
|
+
this.readTenants = tenants;
|
|
245
|
+
}
|
|
240
246
|
// ── Session lifecycle ────────────────────────────────────────────
|
|
241
247
|
async health(req = {}) {
|
|
242
248
|
this.guardOpen();
|
|
@@ -286,7 +292,29 @@ export class LibravDBClient {
|
|
|
286
292
|
}
|
|
287
293
|
async searchTextCollections(req) {
|
|
288
294
|
this.guardOpen();
|
|
289
|
-
|
|
295
|
+
if (this.readTenants.length === 0) {
|
|
296
|
+
return this.client.searchTextCollections(req);
|
|
297
|
+
}
|
|
298
|
+
// Multi-tenant fan-out: search across all read tenants, merge results.
|
|
299
|
+
// Temporarily swap tenantKey per call, restore after.
|
|
300
|
+
const savedKey = this.tenantKey;
|
|
301
|
+
const allResults = [];
|
|
302
|
+
const seen = new Set();
|
|
303
|
+
for (const t of this.readTenants) {
|
|
304
|
+
this.tenantKey = t;
|
|
305
|
+
try {
|
|
306
|
+
const resp = await this.client.searchTextCollections(req);
|
|
307
|
+
for (const r of resp.results ?? []) {
|
|
308
|
+
if (!seen.has(r.id)) {
|
|
309
|
+
seen.add(r.id);
|
|
310
|
+
allResults.push(r);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
catch { /* skip failed tenant reads */ }
|
|
315
|
+
}
|
|
316
|
+
this.tenantKey = savedKey;
|
|
317
|
+
return { results: allResults };
|
|
290
318
|
}
|
|
291
319
|
async listCollection(req) {
|
|
292
320
|
this.guardOpen();
|
package/dist/rules.d.ts
CHANGED
package/dist/rules.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { dirname } from "node:path";
|
|
3
|
-
|
|
3
|
+
let maxRules = 20;
|
|
4
|
+
export function setMaxRules(n) {
|
|
5
|
+
maxRules = Math.max(0, n);
|
|
6
|
+
}
|
|
4
7
|
function jsonResult(details) {
|
|
5
8
|
return { content: [{ type: "text", text: JSON.stringify(details, null, 2) }], details };
|
|
6
9
|
}
|
|
@@ -41,7 +44,7 @@ export function getRule(id) {
|
|
|
41
44
|
}
|
|
42
45
|
export function setRule(ruleText, keywords, priority) {
|
|
43
46
|
let replaced = false;
|
|
44
|
-
if (rules.length >=
|
|
47
|
+
if (maxRules > 0 && rules.length >= maxRules) {
|
|
45
48
|
let minIdx = 0;
|
|
46
49
|
for (let i = 1; i < rules.length; i++) {
|
|
47
50
|
if (rules[i].priority < rules[minIdx].priority)
|
package/dist/types.d.ts
CHANGED
|
@@ -7,9 +7,15 @@ export interface PluginConfig {
|
|
|
7
7
|
* the plugin falls back to the auto-derived userId. Set different values per
|
|
8
8
|
* agent to isolate memory storage. */
|
|
9
9
|
tenantId?: string;
|
|
10
|
-
/** Per-agent tenant overrides. Maps agentId → tenantId
|
|
11
|
-
*
|
|
12
|
-
|
|
10
|
+
/** Per-agent tenant overrides. Maps agentId → tenantId (string) or
|
|
11
|
+
* { primary: string, readAccess?: string[] } (object). Agents not listed
|
|
12
|
+
* fall through to tenantId → userId. Opt-in. */
|
|
13
|
+
tenantIdByAgent?: Record<string, string | {
|
|
14
|
+
primary: string;
|
|
15
|
+
readAccess?: string[];
|
|
16
|
+
}>;
|
|
17
|
+
/** Maximum number of hard constraint rules. Default 20. */
|
|
18
|
+
maxRules?: number;
|
|
13
19
|
/** Stable identity for cross-session durable memory. When set, all sessions
|
|
14
20
|
* share memories under user:{userId}. When unset, the plugin auto-derives
|
|
15
21
|
* identity from the OS and persists it to the identity file. */
|
package/openclaw.plugin.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "libravdb-memory",
|
|
3
3
|
"name": "LibraVDB Memory",
|
|
4
4
|
"description": "Cognitive memory engine — causal graph reasoning, predictive context, identity tracking, and hybrid vector recall with back-door adjustment scoring",
|
|
5
|
-
"version": "1.10.
|
|
5
|
+
"version": "1.10.5",
|
|
6
6
|
"kind": [
|
|
7
7
|
"memory",
|
|
8
8
|
"context-engine"
|
|
@@ -86,6 +86,12 @@
|
|
|
86
86
|
"additionalProperties": { "type": "string" },
|
|
87
87
|
"description": "Per-agent tenant overrides. Maps agentId to tenantId. Agents not listed fall through to tenantId -> userId. Example: {\"jarvis\": \"jarvis\", \"gideon\": \"gideon\"}."
|
|
88
88
|
},
|
|
89
|
+
"maxRules": {
|
|
90
|
+
"type": "number",
|
|
91
|
+
"default": 20,
|
|
92
|
+
"minimum": 0,
|
|
93
|
+
"description": "Maximum hard constraint rules. 0 disables rules. Default 20."
|
|
94
|
+
},
|
|
89
95
|
"userId": {
|
|
90
96
|
"type": "string",
|
|
91
97
|
"description": "Stable identity for cross-session durable memory. When set, sessions share memories under user:{userId}."
|