@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 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
 
@@ -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 byAgent = cfg.tenantIdByAgent[agentId]?.trim();
133
- if (byAgent)
134
- return byAgent;
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 byAgent = cfg.tenantIdByAgent[agentId]?.trim();
16906
- if (byAgent) return byAgent;
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 MAX_RULES = 20;
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 >= MAX_RULES) {
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
- return this.client.searchTextCollections(req);
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) initRuleStore(cacheDir + "/libravdb", logger);
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>;
@@ -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
- return this.client.searchTextCollections(req);
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
@@ -6,6 +6,7 @@ export interface Rule {
6
6
  priority: number;
7
7
  created_at: number;
8
8
  }
9
+ export declare function setMaxRules(n: number): void;
9
10
  type ToolContent = {
10
11
  type: "text";
11
12
  text: string;
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
- const MAX_RULES = 20;
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 >= MAX_RULES) {
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. Agents not in this
11
- * map fall through to tenantId userId. Opt-in. */
12
- tenantIdByAgent?: Record<string, string>;
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. */
@@ -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.3",
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}."
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xdarkicex/openclaw-memory-libravdb",
3
- "version": "1.10.3",
3
+ "version": "1.10.5",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",