@xdarkicex/openclaw-memory-libravdb 1.10.1-beta.6 → 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 +52 -0
- 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,6 +181,33 @@ 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
|
|
|
184
|
+
### PII Scrubbing & Hard Constraint Rules
|
|
185
|
+
|
|
186
|
+
Agent replies are scanned for forbidden keywords before dispatch. Rules are stored
|
|
187
|
+
locally and enforced through two layers:
|
|
188
|
+
|
|
189
|
+
**Agent tools for rules:**
|
|
190
|
+
- `set_rule(rule, keywords, priority)` — create a rule with comma-separated keywords for reply scanning. Max 20 rules.
|
|
191
|
+
- `get_rule(rule_id)` — read a specific rule.
|
|
192
|
+
- `list_rules()` — list all rules sorted by priority.
|
|
193
|
+
- `delete_rule(rule_id)` — remove a rule.
|
|
194
|
+
|
|
195
|
+
**Dual enforcement layers:**
|
|
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").
|
|
198
|
+
|
|
199
|
+
**Example:**
|
|
200
|
+
```
|
|
201
|
+
Rule: "Never reveal my employer"
|
|
202
|
+
Keywords: "acme corp, acmecorp, acme.com"
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
If the agent's reply contains any of those keywords, it's blocked and replaced
|
|
206
|
+
with "I cannot answer that." — regardless of what the model chose to say.
|
|
207
|
+
|
|
208
|
+
**Storage:** persisted to `~/.openclaw/cache/libravdb/rules.json`. Survives
|
|
209
|
+
gateway restarts and plugin updates.
|
|
210
|
+
|
|
183
211
|
### Technical Architecture
|
|
184
212
|
|
|
185
213
|
- **Unified Cognitive Scoring** — mathematically blends cosine similarity with frequency, recency, authored salience, and cognitive authority composite weights (`ω(c)`).
|
|
@@ -303,6 +331,30 @@ If you want to run multiple distinct agents (e.g., a "research-agent" and a "cod
|
|
|
303
331
|
}
|
|
304
332
|
```
|
|
305
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
|
+
|
|
306
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.
|
|
307
359
|
|
|
308
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}."
|