@xdarkicex/openclaw-memory-libravdb 1.10.1 → 1.10.3
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 +30 -5
- package/dist/identity.d.ts +1 -1
- package/dist/identity.js +7 -1
- package/dist/index.js +30 -10
- package/dist/libravdb-client.d.ts +4 -0
- package/dist/libravdb-client.js +13 -7
- package/dist/types.d.ts +3 -0
- package/openclaw.plugin.json +6 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -160,6 +160,7 @@ If your service runs elsewhere, set `sidecarPath`:
|
|
|
160
160
|
- **Memory-mapped embedding cache.** Frequently embedded text is cached in a file-backed mmap region that survives daemon restarts. Cold starts are faster, repeat queries are instant.
|
|
161
161
|
- **Pluggable summarization backend.** The memory kernel's extractive summarization can replace LLM-based compaction — zero tokens burned on summarization.
|
|
162
162
|
- **Local-first inference.** GGUF, ONNX, or remote embedding backends. Hardware-native acceleration on Apple Silicon and NVIDIA. No cloud required.
|
|
163
|
+
- **PII scrubbing & hard constraint rules.** Set rules with keywords — the rules engine scans every reply before dispatch. If a rule keyword is found, the reply is blocked and replaced. No LLM overhead, no latency, can't be bypassed. Names, employers, locations, API keys — anything you don't want leaking gets enforced at the reply layer, not the prompt layer.
|
|
163
164
|
- **Operational CLI.** `libravdbd status`, `health`, `search`, `tenant evict`, `migrate` — live observability and management without interrupting active sessions.
|
|
164
165
|
|
|
165
166
|
### Identity Tracking & User Cards
|
|
@@ -180,10 +181,10 @@ The daemon tracks **who you are** — not just what you said. Every speaker the
|
|
|
180
181
|
- Cards participate in the causal graph (`memory_kind: "identity"`) — identity patterns detected by the cognitive scheduler at macro scale
|
|
181
182
|
- `PredictiveContext` seeds the card node — BFS surfaces causally connected memories alongside identity
|
|
182
183
|
|
|
183
|
-
### Hard Constraint Rules
|
|
184
|
+
### PII Scrubbing & Hard Constraint Rules
|
|
184
185
|
|
|
185
|
-
|
|
186
|
-
|
|
186
|
+
Agent replies are scanned for forbidden keywords before dispatch. Rules are stored
|
|
187
|
+
locally and enforced through two layers:
|
|
187
188
|
|
|
188
189
|
**Agent tools for rules:**
|
|
189
190
|
- `set_rule(rule, keywords, priority)` — create a rule with comma-separated keywords for reply scanning. Max 20 rules.
|
|
@@ -192,8 +193,8 @@ persisted across sessions, and enforced through two complementary mechanisms:
|
|
|
192
193
|
- `delete_rule(rule_id)` — remove a rule.
|
|
193
194
|
|
|
194
195
|
**Dual enforcement layers:**
|
|
195
|
-
- **
|
|
196
|
-
- **
|
|
196
|
+
- **Reply scan (hard enforcement)** — the `before_agent_reply` hook runs `scanReply` on every agent response. If any rule keyword is found anywhere in the reply text, the response is blocked and replaced with "I cannot answer that." This happens at the dispatch layer — the model can't override it, hallucinate around it, or leak PII. Substring match, case-insensitive, zero LLM overhead.
|
|
197
|
+
- **System prompt (behavioral guidance)** — rules are injected at `prependSystemContext` level (AGENTS.md equivalent) so the model follows behavioral constraints ("always use TypeScript", "never delete files without asking").
|
|
197
198
|
|
|
198
199
|
**Example:**
|
|
199
200
|
```
|
|
@@ -330,6 +331,30 @@ If you want to run multiple distinct agents (e.g., a "research-agent" and a "cod
|
|
|
330
331
|
}
|
|
331
332
|
```
|
|
332
333
|
|
|
334
|
+
**Per-agent routing on a single host:** If you run multiple agents through one
|
|
335
|
+
OpenClaw instance, use `tenantIdByAgent` to map each agent to its own tenant:
|
|
336
|
+
|
|
337
|
+
```json
|
|
338
|
+
{
|
|
339
|
+
"plugins": {
|
|
340
|
+
"entries": {
|
|
341
|
+
"libravdb-memory": {
|
|
342
|
+
"enabled": true,
|
|
343
|
+
"config": {
|
|
344
|
+
"tenantIdByAgent": {
|
|
345
|
+
"jarvis": "jarvis",
|
|
346
|
+
"gideon": "gideon",
|
|
347
|
+
"steel": "steel"
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
Unlisted agents fall through to `tenantId` → `userId`. Zero daemon changes required.
|
|
357
|
+
|
|
333
358
|
The memory kernel will seamlessly route the agent's requests to a dedicated, isolated vector database file. It manages all tenant instances efficiently within a single process and automatically shares a centralized, memory-mapped embedding cache to keep hardware usage incredibly low.
|
|
334
359
|
|
|
335
360
|
### Directory Structure
|
package/dist/identity.d.ts
CHANGED
|
@@ -21,4 +21,4 @@ export declare function resolveIdentity(params: {
|
|
|
21
21
|
* 2. LIBRAVDB_AGENT_ID env var (container/CI override)
|
|
22
22
|
* 3. Fall back to resolved userId (existing identity system)
|
|
23
23
|
*/
|
|
24
|
-
export declare function resolveTenantKey(cfg: PluginConfig): string;
|
|
24
|
+
export declare function resolveTenantKey(cfg: PluginConfig, agentId?: string): string;
|
package/dist/identity.js
CHANGED
|
@@ -126,7 +126,13 @@ export function resolveIdentity(params) {
|
|
|
126
126
|
* 2. LIBRAVDB_AGENT_ID env var (container/CI override)
|
|
127
127
|
* 3. Fall back to resolved userId (existing identity system)
|
|
128
128
|
*/
|
|
129
|
-
export function resolveTenantKey(cfg) {
|
|
129
|
+
export function resolveTenantKey(cfg, agentId) {
|
|
130
|
+
// Per-agent override (highest priority).
|
|
131
|
+
if (agentId && cfg.tenantIdByAgent) {
|
|
132
|
+
const byAgent = cfg.tenantIdByAgent[agentId]?.trim();
|
|
133
|
+
if (byAgent)
|
|
134
|
+
return byAgent;
|
|
135
|
+
}
|
|
130
136
|
const explicit = cfg.tenantId?.trim();
|
|
131
137
|
if (explicit)
|
|
132
138
|
return explicit;
|
package/dist/index.js
CHANGED
|
@@ -16900,7 +16900,11 @@ function resolveIdentity(params) {
|
|
|
16900
16900
|
}
|
|
16901
16901
|
return { userId: autoId, source: "auto" };
|
|
16902
16902
|
}
|
|
16903
|
-
function resolveTenantKey(cfg) {
|
|
16903
|
+
function resolveTenantKey(cfg, agentId) {
|
|
16904
|
+
if (agentId && cfg.tenantIdByAgent) {
|
|
16905
|
+
const byAgent = cfg.tenantIdByAgent[agentId]?.trim();
|
|
16906
|
+
if (byAgent) return byAgent;
|
|
16907
|
+
}
|
|
16904
16908
|
const explicit = cfg.tenantId?.trim();
|
|
16905
16909
|
if (explicit) return explicit;
|
|
16906
16910
|
const envId = process.env.LIBRAVDB_AGENT_ID?.trim();
|
|
@@ -35005,6 +35009,7 @@ var LibravDBClient = class {
|
|
|
35005
35009
|
legacyProbeTimeoutMs;
|
|
35006
35010
|
nonceHex;
|
|
35007
35011
|
closed = false;
|
|
35012
|
+
tenantKey;
|
|
35008
35013
|
constructor(options = {}) {
|
|
35009
35014
|
this.secret = options.secret ?? loadSecretFromEnv();
|
|
35010
35015
|
const rawEndpoint = resolveClientEndpoint(options.endpoint);
|
|
@@ -35038,14 +35043,14 @@ var LibravDBClient = class {
|
|
|
35038
35043
|
bootstrap: () => self.bootstrapHandshake(),
|
|
35039
35044
|
rpcMutex
|
|
35040
35045
|
});
|
|
35046
|
+
this.tenantKey = options.tenantKey;
|
|
35041
35047
|
const interceptors = [];
|
|
35042
|
-
|
|
35043
|
-
|
|
35044
|
-
|
|
35045
|
-
|
|
35046
|
-
|
|
35047
|
-
|
|
35048
|
-
}
|
|
35048
|
+
interceptors.push((next) => async (req) => {
|
|
35049
|
+
if (self.tenantKey) {
|
|
35050
|
+
req.header.set("libravdb-tenant-key", self.tenantKey);
|
|
35051
|
+
}
|
|
35052
|
+
return next(req);
|
|
35053
|
+
});
|
|
35049
35054
|
interceptors.push(authInterceptor);
|
|
35050
35055
|
const transport = createGrpcTransport({
|
|
35051
35056
|
baseUrl: targetUrl,
|
|
@@ -35090,6 +35095,11 @@ var LibravDBClient = class {
|
|
|
35090
35095
|
throw new Error("LibravDB client is closed");
|
|
35091
35096
|
}
|
|
35092
35097
|
}
|
|
35098
|
+
/** Update the tenant key for subsequent gRPC calls. Thread-safe —
|
|
35099
|
+
* the interceptor reads this.tenantKey on every request. */
|
|
35100
|
+
setTenantKey(key) {
|
|
35101
|
+
this.tenantKey = key;
|
|
35102
|
+
}
|
|
35093
35103
|
// ── Session lifecycle ────────────────────────────────────────────
|
|
35094
35104
|
async health(req = {}) {
|
|
35095
35105
|
this.guardOpen();
|
|
@@ -35650,9 +35660,19 @@ function register(api) {
|
|
|
35650
35660
|
}
|
|
35651
35661
|
});
|
|
35652
35662
|
api.on("before_prompt_build", async (_event, ctx) => {
|
|
35653
|
-
const
|
|
35654
|
-
const
|
|
35663
|
+
const c = ctx;
|
|
35664
|
+
const sessionId = c?.sessionId;
|
|
35665
|
+
const trigger = c?.trigger;
|
|
35655
35666
|
if (sessionId) setSessionTrigger(sessionId, trigger);
|
|
35667
|
+
if (runtimeOrNull) {
|
|
35668
|
+
const agentId = c?.agentId;
|
|
35669
|
+
const tenantKey = resolveTenantKey(cfg, agentId);
|
|
35670
|
+
try {
|
|
35671
|
+
const client = await runtimeOrNull.getClient();
|
|
35672
|
+
client.setTenantKey(tenantKey);
|
|
35673
|
+
} catch {
|
|
35674
|
+
}
|
|
35675
|
+
}
|
|
35656
35676
|
});
|
|
35657
35677
|
const MULTI_SPEAKER_PROVIDERS = /* @__PURE__ */ new Set([
|
|
35658
35678
|
"discord",
|
|
@@ -35,9 +35,13 @@ export declare class LibravDBClient {
|
|
|
35
35
|
private readonly legacyProbeTimeoutMs;
|
|
36
36
|
private nonceHex;
|
|
37
37
|
private closed;
|
|
38
|
+
private tenantKey;
|
|
38
39
|
constructor(options?: LibravDBClientOptions);
|
|
39
40
|
bootstrapHandshake(): Promise<void>;
|
|
40
41
|
private guardOpen;
|
|
42
|
+
/** Update the tenant key for subsequent gRPC calls. Thread-safe —
|
|
43
|
+
* the interceptor reads this.tenantKey on every request. */
|
|
44
|
+
setTenantKey(key: string): void;
|
|
41
45
|
health(req?: PartialMessage<HealthRequest>): Promise<HealthResponse>;
|
|
42
46
|
status(req?: PartialMessage<MemoryStatusRequest>): Promise<MemoryStatusResponse>;
|
|
43
47
|
flush(req?: PartialMessage<FlushRequest>): Promise<FlushResponse>;
|
package/dist/libravdb-client.js
CHANGED
|
@@ -149,6 +149,7 @@ export class LibravDBClient {
|
|
|
149
149
|
legacyProbeTimeoutMs;
|
|
150
150
|
nonceHex;
|
|
151
151
|
closed = false;
|
|
152
|
+
tenantKey;
|
|
152
153
|
constructor(options = {}) {
|
|
153
154
|
this.secret = options.secret ?? loadSecretFromEnv();
|
|
154
155
|
const rawEndpoint = resolveClientEndpoint(options.endpoint);
|
|
@@ -180,14 +181,14 @@ export class LibravDBClient {
|
|
|
180
181
|
bootstrap: () => self.bootstrapHandshake(),
|
|
181
182
|
rpcMutex,
|
|
182
183
|
});
|
|
184
|
+
this.tenantKey = options.tenantKey;
|
|
183
185
|
const interceptors = [];
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
}
|
|
186
|
+
interceptors.push((next) => async (req) => {
|
|
187
|
+
if (self.tenantKey) {
|
|
188
|
+
req.header.set("libravdb-tenant-key", self.tenantKey);
|
|
189
|
+
}
|
|
190
|
+
return next(req);
|
|
191
|
+
});
|
|
191
192
|
interceptors.push(authInterceptor);
|
|
192
193
|
const transport = createGrpcTransport({
|
|
193
194
|
baseUrl: targetUrl,
|
|
@@ -231,6 +232,11 @@ export class LibravDBClient {
|
|
|
231
232
|
throw new Error("LibravDB client is closed");
|
|
232
233
|
}
|
|
233
234
|
}
|
|
235
|
+
/** Update the tenant key for subsequent gRPC calls. Thread-safe —
|
|
236
|
+
* the interceptor reads this.tenantKey on every request. */
|
|
237
|
+
setTenantKey(key) {
|
|
238
|
+
this.tenantKey = key;
|
|
239
|
+
}
|
|
234
240
|
// ── Session lifecycle ────────────────────────────────────────────
|
|
235
241
|
async health(req = {}) {
|
|
236
242
|
this.guardOpen();
|
package/dist/types.d.ts
CHANGED
|
@@ -7,6 +7,9 @@ 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
13
|
/** Stable identity for cross-session durable memory. When set, all sessions
|
|
11
14
|
* share memories under user:{userId}. When unset, the plugin auto-derives
|
|
12
15
|
* 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.3",
|
|
6
6
|
"kind": [
|
|
7
7
|
"memory",
|
|
8
8
|
"context-engine"
|
|
@@ -81,6 +81,11 @@
|
|
|
81
81
|
"type": "string",
|
|
82
82
|
"description": "Stable tenant identifier for multi-agent deployments. When set, the daemon routes this plugin instance to an isolated vector database. When unset, the plugin falls back to the auto-derived userId. Set different values per agent to isolate memory storage."
|
|
83
83
|
},
|
|
84
|
+
"tenantIdByAgent": {
|
|
85
|
+
"type": "object",
|
|
86
|
+
"additionalProperties": { "type": "string" },
|
|
87
|
+
"description": "Per-agent tenant overrides. Maps agentId to tenantId. Agents not listed fall through to tenantId -> userId. Example: {\"jarvis\": \"jarvis\", \"gideon\": \"gideon\"}."
|
|
88
|
+
},
|
|
84
89
|
"userId": {
|
|
85
90
|
"type": "string",
|
|
86
91
|
"description": "Stable identity for cross-session durable memory. When set, sessions share memories under user:{userId}."
|