@xdarkicex/openclaw-memory-libravdb 1.4.16 → 1.4.18
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 +107 -363
- package/cli-metadata.js +1 -0
- package/dist/cli-descriptors.d.ts +9 -0
- package/dist/cli-descriptors.js +18 -0
- package/dist/cli-metadata.d.ts +9 -0
- package/dist/cli-metadata.js +11 -0
- package/dist/cli.d.ts +3 -10
- package/dist/cli.js +202 -13
- package/dist/index.d.ts +2 -0
- package/dist/index.js +54 -25
- package/dist/memory-runtime.d.ts +6 -1
- package/dist/memory-runtime.js +13 -5
- package/docs/README.md +31 -16
- package/docs/assets/libravdb-logo.svg +14 -0
- package/docs/contributing.md +16 -69
- package/docs/development.md +98 -0
- package/docs/features.md +127 -0
- package/docs/install.md +4 -0
- package/docs/installation.md +79 -272
- package/docs/models.md +37 -46
- package/docs/performance-and-tuning.md +145 -0
- package/openclaw.plugin.json +6 -1
- package/package.json +2 -1
package/dist/cli.js
CHANGED
|
@@ -1,17 +1,68 @@
|
|
|
1
1
|
import { createInterface } from "node:readline/promises";
|
|
2
2
|
import { stdin, stdout } from "node:process";
|
|
3
|
+
import { MEMORY_CLI_DESCRIPTOR, isMemorySlotSelected } from "./cli-descriptors.js";
|
|
3
4
|
import { resolveDurableNamespace } from "./durable-namespace.js";
|
|
4
5
|
import { promoteDreamDiaryFile } from "./dream-promotion.js";
|
|
6
|
+
import { buildMemoryRuntimeBridge } from "./memory-runtime.js";
|
|
5
7
|
export function registerMemoryCli(api, runtime, cfg, logger = console) {
|
|
6
8
|
if (!api.registerCli) {
|
|
7
9
|
return;
|
|
8
10
|
}
|
|
11
|
+
if (!isMemorySlotSelected(api)) {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
const isFullMode = runtime !== null;
|
|
9
15
|
api.registerCli(({ program }) => {
|
|
10
16
|
const root = ensureCommand(program, "memory")
|
|
11
17
|
.description("Manage LibraVDB memory");
|
|
18
|
+
if (!isFullMode) {
|
|
19
|
+
// Non-full modes register structure only so `openclaw memory --help` works.
|
|
20
|
+
// No runtime available — do not attach action handlers.
|
|
21
|
+
ensureCommand(root, "status").description("Show sidecar health, record counts, and active thresholds");
|
|
22
|
+
ensureCommand(root, "index").description("Refresh delegated LibraVDB memory index state");
|
|
23
|
+
ensureCommand(root, "search").description("Search LibraVDB memory");
|
|
24
|
+
ensureCommand(root, "flush").description("Wipe a durable memory namespace after confirmation");
|
|
25
|
+
ensureCommand(root, "export").description("Stream stored memories as newline-delimited JSON");
|
|
26
|
+
ensureCommand(root, "journal").description("Inspect internal lifecycle journal hints");
|
|
27
|
+
ensureCommand(root, "dream-promote").description("Promote vetted dream diary entries into the dedicated dream collection");
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
12
30
|
ensureCommand(root, "status")
|
|
13
31
|
.description("Show sidecar health, record counts, and active thresholds")
|
|
14
|
-
.
|
|
32
|
+
.option("--agent <id>", "Agent id")
|
|
33
|
+
.option("--json", "Print JSON")
|
|
34
|
+
.option("--deep", "Probe daemon readiness")
|
|
35
|
+
.option("--index", "Refresh delegated index state before printing status")
|
|
36
|
+
.option("--fix", "Accepted for OpenClaw memory CLI compatibility")
|
|
37
|
+
.option("--verbose", "Verbose logging")
|
|
38
|
+
.action(async (opts) => {
|
|
39
|
+
await runCliCommand(runtime, logger, async () => {
|
|
40
|
+
await runStatus(runtime, cfg, logger, normalizeOptionBag(opts));
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
ensureCommand(root, "index")
|
|
44
|
+
.description("Refresh delegated LibraVDB memory index state")
|
|
45
|
+
.option("--agent <id>", "Agent id")
|
|
46
|
+
.option("--force", "Force refresh where supported")
|
|
47
|
+
.option("--verbose", "Verbose logging")
|
|
48
|
+
.action(async (opts) => {
|
|
49
|
+
await runCliCommand(runtime, logger, async () => {
|
|
50
|
+
await runIndex(runtime, cfg, normalizeOptionBag(opts), logger);
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
const search = ensureCommand(root, "search")
|
|
54
|
+
.description("Search LibraVDB memory")
|
|
55
|
+
.option("--query <text>", "Search query (alternative to positional argument)")
|
|
56
|
+
.option("--agent <id>", "Agent id")
|
|
57
|
+
.option("--max-results <n>", "Max results")
|
|
58
|
+
.option("--min-score <n>", "Minimum score")
|
|
59
|
+
.option("--json", "Print JSON");
|
|
60
|
+
search.argument?.("[query]", "Search query");
|
|
61
|
+
search.action(async (queryOrOpts, maybeOpts) => {
|
|
62
|
+
await runCliCommand(runtime, logger, async () => {
|
|
63
|
+
await runSearch(runtime, cfg, normalizeQueryArg(queryOrOpts), normalizeActionOptions(queryOrOpts, maybeOpts), logger);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
15
66
|
const flush = ensureCommand(root, "flush")
|
|
16
67
|
.description("Wipe a durable memory namespace after confirmation");
|
|
17
68
|
if (flush.requiredOption) {
|
|
@@ -23,17 +74,29 @@ export function registerMemoryCli(api, runtime, cfg, logger = console) {
|
|
|
23
74
|
flush.option("--session-key <sessionKey>", "Session key whose derived durable namespace should be deleted");
|
|
24
75
|
flush
|
|
25
76
|
.option("--yes", "Skip the confirmation prompt")
|
|
26
|
-
.action((opts) =>
|
|
77
|
+
.action(async (opts) => {
|
|
78
|
+
await runCliCommand(runtime, logger, async () => {
|
|
79
|
+
await runFlush(runtime, normalizeOptionBag(opts), logger);
|
|
80
|
+
});
|
|
81
|
+
});
|
|
27
82
|
const exportCmd = ensureCommand(root, "export")
|
|
28
83
|
.description("Stream stored memories as newline-delimited JSON");
|
|
29
84
|
exportCmd.option("--user-id <userId>", "Restrict export to a single user namespace");
|
|
30
85
|
exportCmd.option("--session-key <sessionKey>", "Restrict export to a derived session-key namespace");
|
|
31
|
-
exportCmd.action((opts) =>
|
|
86
|
+
exportCmd.action(async (opts) => {
|
|
87
|
+
await runCliCommand(runtime, logger, async () => {
|
|
88
|
+
await runExport(runtime, normalizeOptionBag(opts), logger);
|
|
89
|
+
});
|
|
90
|
+
});
|
|
32
91
|
const journal = ensureCommand(root, "journal")
|
|
33
92
|
.description("Inspect internal lifecycle journal hints");
|
|
34
93
|
journal.option("--session-id <sessionId>", "Restrict journal entries to one session id");
|
|
35
94
|
journal.option("--limit <limit>", "Maximum journal entries to show");
|
|
36
|
-
journal.action((opts) =>
|
|
95
|
+
journal.action(async (opts) => {
|
|
96
|
+
await runCliCommand(runtime, logger, async () => {
|
|
97
|
+
await runJournal(runtime, normalizeOptionBag(opts), logger);
|
|
98
|
+
});
|
|
99
|
+
});
|
|
37
100
|
const dreamPromote = ensureCommand(root, "dream-promote")
|
|
38
101
|
.description("Promote vetted dream diary entries into the dedicated dream collection");
|
|
39
102
|
if (dreamPromote.requiredOption) {
|
|
@@ -44,15 +107,13 @@ export function registerMemoryCli(api, runtime, cfg, logger = console) {
|
|
|
44
107
|
dreamPromote.option("--user-id <userId>", "User id whose dream collection should receive the promotion");
|
|
45
108
|
dreamPromote.option("--dream-file <path>", "Dream diary markdown file to promote from");
|
|
46
109
|
}
|
|
47
|
-
dreamPromote.action((opts) =>
|
|
110
|
+
dreamPromote.action(async (opts) => {
|
|
111
|
+
await runCliCommand(runtime, logger, async () => {
|
|
112
|
+
await runDreamPromote(runtime, normalizeOptionBag(opts), logger);
|
|
113
|
+
});
|
|
114
|
+
});
|
|
48
115
|
}, {
|
|
49
|
-
descriptors: [
|
|
50
|
-
{
|
|
51
|
-
name: "memory",
|
|
52
|
-
description: "Manage LibraVDB memory",
|
|
53
|
-
hasSubcommands: true,
|
|
54
|
-
},
|
|
55
|
-
],
|
|
116
|
+
descriptors: [MEMORY_CLI_DESCRIPTOR],
|
|
56
117
|
});
|
|
57
118
|
}
|
|
58
119
|
function ensureCommand(parent, name) {
|
|
@@ -67,10 +128,30 @@ function ensureCommand(parent, name) {
|
|
|
67
128
|
}
|
|
68
129
|
return parent.command(name);
|
|
69
130
|
}
|
|
70
|
-
async function
|
|
131
|
+
async function runCliCommand(runtime, logger, action) {
|
|
132
|
+
try {
|
|
133
|
+
await action();
|
|
134
|
+
}
|
|
135
|
+
finally {
|
|
136
|
+
try {
|
|
137
|
+
await runtime.shutdown();
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
logger.warn?.(`LibraVDB CLI shutdown failed: ${formatError(error)}`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
async function runStatus(runtime, cfg, logger, opts = {}) {
|
|
145
|
+
if (opts.index) {
|
|
146
|
+
await runIndex(runtime, cfg, { ...opts, verbose: false }, logger, { quiet: true });
|
|
147
|
+
}
|
|
71
148
|
try {
|
|
72
149
|
const rpc = await runtime.getRpc();
|
|
73
150
|
const status = await rpc.call("status", {});
|
|
151
|
+
if (opts.json) {
|
|
152
|
+
console.log(JSON.stringify({ status }, null, 2));
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
74
155
|
console.table({
|
|
75
156
|
Sidecar: status.ok ? "running" : "down",
|
|
76
157
|
"Turns stored": status.turnCount ?? 0,
|
|
@@ -84,6 +165,17 @@ async function runStatus(runtime, cfg, logger) {
|
|
|
84
165
|
}
|
|
85
166
|
catch (error) {
|
|
86
167
|
logger.error(`LibraVDB status unavailable: ${formatError(error)}`);
|
|
168
|
+
if (opts.json) {
|
|
169
|
+
console.log(JSON.stringify({
|
|
170
|
+
status: {
|
|
171
|
+
ok: false,
|
|
172
|
+
message: formatError(error),
|
|
173
|
+
gatingThreshold: cfg.ingestionGateThreshold ?? 0.35,
|
|
174
|
+
},
|
|
175
|
+
}, null, 2));
|
|
176
|
+
process.exitCode = 1;
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
87
179
|
console.table({
|
|
88
180
|
Sidecar: "down",
|
|
89
181
|
"Turns stored": "n/a",
|
|
@@ -97,6 +189,79 @@ async function runStatus(runtime, cfg, logger) {
|
|
|
97
189
|
process.exitCode = 1;
|
|
98
190
|
}
|
|
99
191
|
}
|
|
192
|
+
async function runIndex(runtime, cfg, opts, logger, params = {}) {
|
|
193
|
+
try {
|
|
194
|
+
const bridge = buildMemoryRuntimeBridge(runtime.getRpc, cfg);
|
|
195
|
+
const { manager } = await bridge.getMemorySearchManager({
|
|
196
|
+
agentId: opts?.agent,
|
|
197
|
+
purpose: "status",
|
|
198
|
+
});
|
|
199
|
+
await manager.sync?.({
|
|
200
|
+
reason: "cli",
|
|
201
|
+
force: Boolean(opts?.force),
|
|
202
|
+
});
|
|
203
|
+
const status = manager.status();
|
|
204
|
+
if (status.ok === false) {
|
|
205
|
+
logger.error(`LibraVDB index refresh unavailable: ${status.message ?? "sidecar unavailable"}`);
|
|
206
|
+
process.exitCode = 1;
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
if (opts?.verbose && !params.quiet) {
|
|
210
|
+
console.table({
|
|
211
|
+
Provider: status.provider ?? "libravdb",
|
|
212
|
+
Model: status.model ?? status.embeddingProfile ?? "unknown",
|
|
213
|
+
"Turns stored": status.turnCount ?? 0,
|
|
214
|
+
"Memories stored": status.memoryCount ?? 0,
|
|
215
|
+
Message: status.message ?? "ok",
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
if (!params.quiet) {
|
|
219
|
+
console.log("LibraVDB memory index refresh delegated to the sidecar.");
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
catch (error) {
|
|
223
|
+
logger.error(`LibraVDB index refresh failed: ${formatError(error)}`);
|
|
224
|
+
process.exitCode = 1;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
async function runSearch(runtime, cfg, queryArg, opts, logger) {
|
|
228
|
+
const query = opts?.query?.trim() || queryArg?.trim();
|
|
229
|
+
if (!query) {
|
|
230
|
+
logger.error("LibraVDB search requires a query. Provide a positional query or --query <text>.");
|
|
231
|
+
process.exitCode = 1;
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
try {
|
|
235
|
+
const bridge = buildMemoryRuntimeBridge(runtime.getRpc, cfg);
|
|
236
|
+
const { manager } = await bridge.getMemorySearchManager({
|
|
237
|
+
agentId: opts?.agent,
|
|
238
|
+
});
|
|
239
|
+
const maxResults = normalizeLimit(opts?.maxResults ?? opts?.limit);
|
|
240
|
+
const minScore = normalizeNumber(opts?.minScore);
|
|
241
|
+
const results = (await manager.search({
|
|
242
|
+
query,
|
|
243
|
+
...(maxResults ? { maxResults } : {}),
|
|
244
|
+
...(minScore !== undefined ? { minScore } : {}),
|
|
245
|
+
}));
|
|
246
|
+
if (opts?.json) {
|
|
247
|
+
console.log(JSON.stringify({ results }, null, 2));
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
if (results.length === 0) {
|
|
251
|
+
console.log("No matches.");
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
for (const result of results) {
|
|
255
|
+
console.log(`${result.score.toFixed(3)} ${result.path}:${result.startLine}-${result.endLine}`);
|
|
256
|
+
console.log(result.snippet);
|
|
257
|
+
console.log("");
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
catch (error) {
|
|
261
|
+
logger.error(`LibraVDB search failed: ${formatError(error)}`);
|
|
262
|
+
process.exitCode = 1;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
100
265
|
async function runFlush(runtime, opts, logger) {
|
|
101
266
|
const namespace = resolveCliNamespace(opts);
|
|
102
267
|
if (!namespace) {
|
|
@@ -198,6 +363,30 @@ function normalizeLimit(limit) {
|
|
|
198
363
|
}
|
|
199
364
|
return undefined;
|
|
200
365
|
}
|
|
366
|
+
function normalizeNumber(value) {
|
|
367
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
368
|
+
return value;
|
|
369
|
+
}
|
|
370
|
+
if (typeof value === "string") {
|
|
371
|
+
const parsed = Number.parseFloat(value);
|
|
372
|
+
if (Number.isFinite(parsed)) {
|
|
373
|
+
return parsed;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
return undefined;
|
|
377
|
+
}
|
|
378
|
+
function normalizeOptionBag(value) {
|
|
379
|
+
return value && typeof value === "object" ? value : {};
|
|
380
|
+
}
|
|
381
|
+
function normalizeActionOptions(queryOrOpts, maybeOpts) {
|
|
382
|
+
if (maybeOpts && typeof maybeOpts === "object") {
|
|
383
|
+
return maybeOpts;
|
|
384
|
+
}
|
|
385
|
+
return normalizeOptionBag(queryOrOpts);
|
|
386
|
+
}
|
|
387
|
+
function normalizeQueryArg(value) {
|
|
388
|
+
return typeof value === "string" ? value : undefined;
|
|
389
|
+
}
|
|
201
390
|
function resolveCliNamespace(opts) {
|
|
202
391
|
const userId = opts?.userId?.trim();
|
|
203
392
|
const sessionKey = opts?.sessionKey?.trim();
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
|
|
2
2
|
import { registerMemoryCli } from "./cli.js";
|
|
3
|
+
import { registerMemoryCliMetadata } from "./cli-descriptors.js";
|
|
3
4
|
import { buildContextEngineFactory } from "./context-engine.js";
|
|
4
5
|
import { createBeforeResetHook, createSessionEndHook } from "./lifecycle-hooks.js";
|
|
5
6
|
import { createDreamPromotionHandle } from "./dream-promotion.js";
|
|
@@ -8,33 +9,61 @@ import { buildMemoryPromptSection } from "./memory-provider.js";
|
|
|
8
9
|
import { buildMemoryRuntimeBridge } from "./memory-runtime.js";
|
|
9
10
|
import { createRecallCache } from "./recall-cache.js";
|
|
10
11
|
import { createPluginRuntime } from "./plugin-runtime.js";
|
|
12
|
+
export const MEMORY_ID = "libravdb-memory";
|
|
13
|
+
export function register(api) {
|
|
14
|
+
if (api.registrationMode === "cli-metadata") {
|
|
15
|
+
registerMemoryCliMetadata(api);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const isFullMode = api.registrationMode === "full";
|
|
19
|
+
const cfg = api.pluginConfig;
|
|
20
|
+
// Null in non-full mode: cli.ts skips action handlers when runtime is null.
|
|
21
|
+
const runtimeOrNull = isFullMode
|
|
22
|
+
? createPluginRuntime(cfg, api.logger ?? console)
|
|
23
|
+
: null;
|
|
24
|
+
registerMemoryCli(api, runtimeOrNull, cfg, api.logger ?? console);
|
|
25
|
+
if (!isFullMode)
|
|
26
|
+
return;
|
|
27
|
+
// TypeScript can't narrow through the ternary, so re-bind and guard.
|
|
28
|
+
const runtime = runtimeOrNull;
|
|
29
|
+
if (!runtime)
|
|
30
|
+
return; // unreachable but satisfies the type checker
|
|
31
|
+
const recallCache = createRecallCache();
|
|
32
|
+
// Exclusive slot check: refuse to register if another plugin owns the memory slot.
|
|
33
|
+
// plugins.slots.memory is the only configurable slot; context engine exclusivity
|
|
34
|
+
// is enforced by the registry at runtime (no config surface for it).
|
|
35
|
+
// "none" means memory is disabled, not a conflict, allow registration.
|
|
36
|
+
const memSlot = api.config?.plugins?.slots?.memory;
|
|
37
|
+
if (memSlot && memSlot !== MEMORY_ID && memSlot !== "none") {
|
|
38
|
+
throw new Error(`[libravdb-memory] plugins.slots.memory is "${memSlot}". ` +
|
|
39
|
+
`Set it to "libravdb-memory" before enabling this plugin.`);
|
|
40
|
+
}
|
|
41
|
+
// Migrated from three legacy calls to a single registerMemoryCapability.
|
|
42
|
+
api.registerMemoryCapability(MEMORY_ID, {
|
|
43
|
+
promptBuilder: buildMemoryPromptSection(runtime.getRpc, cfg, recallCache),
|
|
44
|
+
runtime: buildMemoryRuntimeBridge(runtime.getRpc, cfg),
|
|
45
|
+
});
|
|
46
|
+
api.registerContextEngine(MEMORY_ID, () => buildContextEngineFactory(runtime, cfg, recallCache, api.logger ?? console));
|
|
47
|
+
const markdownIngestion = createMarkdownIngestionHandle(cfg, runtime.getRpc, api.logger ?? console);
|
|
48
|
+
const dreamPromotion = createDreamPromotionHandle(cfg, runtime.getRpc, api.logger ?? console);
|
|
49
|
+
void markdownIngestion.start().catch((error) => {
|
|
50
|
+
api.logger?.warn?.(`LibraVDB markdown ingestion failed to start: ${error instanceof Error ? error.message : String(error)}`);
|
|
51
|
+
});
|
|
52
|
+
void dreamPromotion.start().catch((error) => {
|
|
53
|
+
api.logger?.warn?.(`LibraVDB dream promotion failed to start: ${error instanceof Error ? error.message : String(error)}`);
|
|
54
|
+
});
|
|
55
|
+
api.on("before_reset", createBeforeResetHook(runtime, api.logger ?? console));
|
|
56
|
+
api.on("session_end", createSessionEndHook(runtime, api.logger ?? console));
|
|
57
|
+
api.on("gateway_stop", async () => {
|
|
58
|
+
await dreamPromotion.stop();
|
|
59
|
+
await markdownIngestion.stop();
|
|
60
|
+
await runtime.shutdown();
|
|
61
|
+
});
|
|
62
|
+
}
|
|
11
63
|
export default definePluginEntry({
|
|
12
|
-
id:
|
|
64
|
+
id: MEMORY_ID,
|
|
13
65
|
name: "LibraVDB Memory",
|
|
14
66
|
description: "Persistent vector memory with three-tier hybrid scoring",
|
|
15
67
|
kind: ["memory", "context-engine"],
|
|
16
|
-
register
|
|
17
|
-
const cfg = api.pluginConfig;
|
|
18
|
-
const recallCache = createRecallCache();
|
|
19
|
-
const runtime = createPluginRuntime(cfg, api.logger ?? console);
|
|
20
|
-
const markdownIngestion = createMarkdownIngestionHandle(cfg, runtime.getRpc, api.logger ?? console);
|
|
21
|
-
const dreamPromotion = createDreamPromotionHandle(cfg, runtime.getRpc, api.logger ?? console);
|
|
22
|
-
void markdownIngestion.start().catch((error) => {
|
|
23
|
-
api.logger?.warn?.(`LibraVDB markdown ingestion failed to start: ${error instanceof Error ? error.message : String(error)}`);
|
|
24
|
-
});
|
|
25
|
-
void dreamPromotion.start().catch((error) => {
|
|
26
|
-
api.logger?.warn?.(`LibraVDB dream promotion failed to start: ${error instanceof Error ? error.message : String(error)}`);
|
|
27
|
-
});
|
|
28
|
-
registerMemoryCli(api, runtime, cfg, api.logger ?? console);
|
|
29
|
-
api.registerContextEngine("libravdb-memory", () => buildContextEngineFactory(runtime, cfg, recallCache, api.logger ?? console));
|
|
30
|
-
api.registerMemoryPromptSection(buildMemoryPromptSection(runtime.getRpc, cfg, recallCache));
|
|
31
|
-
api.registerMemoryRuntime?.(buildMemoryRuntimeBridge(runtime.getRpc, cfg));
|
|
32
|
-
api.on("before_reset", createBeforeResetHook(runtime, api.logger ?? console));
|
|
33
|
-
api.on("session_end", createSessionEndHook(runtime, api.logger ?? console));
|
|
34
|
-
api.on("gateway_stop", async () => {
|
|
35
|
-
await dreamPromotion.stop();
|
|
36
|
-
await markdownIngestion.stop();
|
|
37
|
-
await runtime.shutdown();
|
|
38
|
-
});
|
|
39
|
-
},
|
|
68
|
+
register,
|
|
40
69
|
});
|
package/dist/memory-runtime.d.ts
CHANGED
|
@@ -7,6 +7,8 @@ type MemorySearchParams = {
|
|
|
7
7
|
q?: string;
|
|
8
8
|
k?: number;
|
|
9
9
|
limit?: number;
|
|
10
|
+
maxResults?: number;
|
|
11
|
+
minScore?: number;
|
|
10
12
|
topK?: number;
|
|
11
13
|
userId?: string;
|
|
12
14
|
agentId?: string;
|
|
@@ -97,7 +99,10 @@ export declare function buildMemoryRuntimeBridge(getRpc: RpcGetter, cfg: PluginC
|
|
|
97
99
|
ingested: boolean;
|
|
98
100
|
delegatedToContextEngine: boolean;
|
|
99
101
|
}>;
|
|
100
|
-
sync(
|
|
102
|
+
sync(_params?: {
|
|
103
|
+
reason?: string;
|
|
104
|
+
force?: boolean;
|
|
105
|
+
}): Promise<{
|
|
101
106
|
synced: boolean;
|
|
102
107
|
delegatedToContextEngine: boolean;
|
|
103
108
|
}>;
|
package/dist/memory-runtime.js
CHANGED
|
@@ -26,7 +26,8 @@ function createMemorySearchManager(getRpc, cfg, defaults, initialStatus) {
|
|
|
26
26
|
const params = legacyCall
|
|
27
27
|
? {
|
|
28
28
|
query: queryOrParams,
|
|
29
|
-
limit: opts.limit ?? opts.k ?? opts.topK,
|
|
29
|
+
limit: opts.limit ?? opts.k ?? opts.maxResults ?? opts.topK,
|
|
30
|
+
minScore: opts.minScore,
|
|
30
31
|
sessionId: opts.sessionId,
|
|
31
32
|
sessionKey: opts.sessionKey,
|
|
32
33
|
userId: opts.userId,
|
|
@@ -46,7 +47,8 @@ function createMemorySearchManager(getRpc, cfg, defaults, initialStatus) {
|
|
|
46
47
|
agentId: firstString(params.agentId, params.context?.agentId, defaults.agentId),
|
|
47
48
|
fallback: sessionId ? `session:${sessionId}` : undefined,
|
|
48
49
|
});
|
|
49
|
-
const k = normalizePositiveInteger(params.k, params.limit, params.topK, cfg.topK, 8);
|
|
50
|
+
const k = normalizePositiveInteger(params.k, params.limit, params.maxResults, params.topK, cfg.topK, 8);
|
|
51
|
+
const minScore = normalizeNumber(params.minScore);
|
|
50
52
|
const rpc = await getRpc();
|
|
51
53
|
const result = dreamQuery.active
|
|
52
54
|
? await rpc.call("search_text", {
|
|
@@ -55,14 +57,17 @@ function createMemorySearchManager(getRpc, cfg, defaults, initialStatus) {
|
|
|
55
57
|
k,
|
|
56
58
|
})
|
|
57
59
|
: await searchResolvedCollections(rpc, cfg, userId, sessionId, queryText, k);
|
|
58
|
-
const
|
|
60
|
+
const filteredResults = minScore === undefined
|
|
61
|
+
? result.results
|
|
62
|
+
: result.results.filter((item) => item.score >= minScore);
|
|
63
|
+
const legacyResults = filteredResults.map((item) => ({
|
|
59
64
|
...item,
|
|
60
65
|
content: item.text,
|
|
61
66
|
}));
|
|
62
67
|
if (legacyCall) {
|
|
63
68
|
return { results: legacyResults };
|
|
64
69
|
}
|
|
65
|
-
return
|
|
70
|
+
return filteredResults.map(toMemorySearchResult);
|
|
66
71
|
},
|
|
67
72
|
async readFile(params) {
|
|
68
73
|
const located = await loadSearchResultText(getRpc, params.relPath);
|
|
@@ -79,7 +84,7 @@ function createMemorySearchManager(getRpc, cfg, defaults, initialStatus) {
|
|
|
79
84
|
// The plugin already owns per-turn ingest through the context engine.
|
|
80
85
|
return { ingested: false, delegatedToContextEngine: true };
|
|
81
86
|
},
|
|
82
|
-
async sync() {
|
|
87
|
+
async sync(_params) {
|
|
83
88
|
cachedStatus = await readStatus(getRpc, defaults.purpose);
|
|
84
89
|
return { synced: true, delegatedToContextEngine: true };
|
|
85
90
|
},
|
|
@@ -215,3 +220,6 @@ function normalizePositiveInteger(...values) {
|
|
|
215
220
|
}
|
|
216
221
|
return 8;
|
|
217
222
|
}
|
|
223
|
+
function normalizeNumber(value) {
|
|
224
|
+
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
225
|
+
}
|
package/docs/README.md
CHANGED
|
@@ -1,16 +1,31 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
This
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
- [
|
|
10
|
-
- [
|
|
11
|
-
- [
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
- [
|
|
16
|
-
- [
|
|
1
|
+
# LibraVDB Memory Docs
|
|
2
|
+
|
|
3
|
+
This directory holds the operational and design docs for the LibraVDB Memory
|
|
4
|
+
OpenClaw plugin. The root README is the public entry point; these files go
|
|
5
|
+
deeper by goal.
|
|
6
|
+
|
|
7
|
+
## Start Here
|
|
8
|
+
|
|
9
|
+
- [Install](./install.md) - shortest supported install and daemon lifecycle path.
|
|
10
|
+
- [Installation reference](./installation.md) - requirements, activation, verification, and troubleshooting.
|
|
11
|
+
- [Uninstall](./uninstall.md) - safe disable, daemon shutdown, package removal, and optional data cleanup.
|
|
12
|
+
|
|
13
|
+
## Understand The System
|
|
14
|
+
|
|
15
|
+
- [Problem](./problem.md) - why this plugin replaces the stock memory lifecycle.
|
|
16
|
+
- [Architecture](./architecture.md) - plugin, sidecar, storage, retrieval, and compaction overview.
|
|
17
|
+
- [Dependency rationale](./dependencies.md) - why LibraVDB and slab-style storage fit this workload.
|
|
18
|
+
- [Architecture decisions](./architecture-decisions/README.md) - accepted ADRs.
|
|
19
|
+
|
|
20
|
+
## Configure And Operate
|
|
21
|
+
|
|
22
|
+
- [Features](./features.md) - markdown ingestion, Obsidian ingestion, dream promotion, and memory CLI commands.
|
|
23
|
+
- [Security](./security.md) - trust boundaries, untrusted-memory framing, collection isolation, and deletion limits.
|
|
24
|
+
- [Embedding profiles](./embedding-profiles.md) - shipped embedding profile metadata and defaults.
|
|
25
|
+
- [Models](./models.md) - local ONNX model strategy and summarization roles.
|
|
26
|
+
|
|
27
|
+
## Advanced And Source Docs
|
|
28
|
+
|
|
29
|
+
- [Performance and tuning](./performance-and-tuning.md) - resource expectations and tuning knobs.
|
|
30
|
+
- [Development](./development.md) - source setup, local daemon builds, generated IPC files, and validation commands.
|
|
31
|
+
- [Contributing](./contributing.md) - contributor workflow and repository expectations.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="1280" height="170" viewBox="0 0 1280 170" role="img" aria-labelledby="title desc">
|
|
2
|
+
<title id="title">LibraVDB</title>
|
|
3
|
+
<desc id="desc">Dark purple LibraVDB wordmark.</desc>
|
|
4
|
+
<rect width="1280" height="170" fill="none"/>
|
|
5
|
+
<text
|
|
6
|
+
x="640"
|
|
7
|
+
y="128"
|
|
8
|
+
text-anchor="middle"
|
|
9
|
+
fill="#5B21B6"
|
|
10
|
+
font-family="Inter, Avenir Next, Helvetica Neue, Arial, sans-serif"
|
|
11
|
+
font-size="150"
|
|
12
|
+
font-weight="800"
|
|
13
|
+
letter-spacing="0">LibraVDB</text>
|
|
14
|
+
</svg>
|
package/docs/contributing.md
CHANGED
|
@@ -1,88 +1,35 @@
|
|
|
1
1
|
# Contributing
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Use [Development](./development.md) for source setup, local daemon preparation,
|
|
4
|
+
generated IPC files, and validation commands. This document covers contribution
|
|
5
|
+
expectations.
|
|
4
6
|
|
|
5
|
-
|
|
6
|
-
- `pnpm`
|
|
7
|
-
- OpenClaw CLI for end-to-end plugin testing
|
|
7
|
+
## Baseline Checks
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
TypeScript and unit checks:
|
|
9
|
+
Before opening a PR:
|
|
12
10
|
|
|
13
11
|
```bash
|
|
14
12
|
pnpm check
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
Integration tests:
|
|
18
|
-
|
|
19
|
-
```bash
|
|
20
|
-
npm run test:integration
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
Plugin integration tests:
|
|
24
|
-
|
|
25
|
-
```bash
|
|
26
13
|
npm run test:integration
|
|
27
14
|
```
|
|
28
15
|
|
|
29
|
-
|
|
16
|
+
Integration tests require a running daemon or a prepared local daemon binary.
|
|
17
|
+
Use:
|
|
30
18
|
|
|
31
19
|
```bash
|
|
32
20
|
bash scripts/build-daemon.sh
|
|
33
21
|
```
|
|
34
22
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
Supported inputs:
|
|
38
|
-
|
|
39
|
-
- installed daemon on `PATH` such as `brew install libravdbd`
|
|
40
|
-
- `LIBRAVDBD_BINARY_PATH=/path/to/libravdbd`
|
|
41
|
-
- `LIBRAVDBD_SOURCE_DIR=/path/to/libravdbd` to build from your private local daemon repo
|
|
42
|
-
|
|
43
|
-
For daemon-internal Go development and release work, use the separate `libravdbd` repository.
|
|
23
|
+
## Behavioral Changes
|
|
44
24
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
- empty-memory novelty
|
|
50
|
-
- saturation veto
|
|
51
|
-
- convex boundedness
|
|
52
|
-
- conversational collapse at `T = 0`
|
|
53
|
-
- technical collapse at `T = 1`
|
|
54
|
-
- non-overfiring conversational structure on code
|
|
55
|
-
|
|
56
|
-
If you add a new signal, it must preserve those invariants.
|
|
57
|
-
|
|
58
|
-
## Calibration Coverage
|
|
59
|
-
|
|
60
|
-
There is not yet a dedicated `gate_calibration_test.go` golden set in the
|
|
61
|
-
repository. Current gating correctness is enforced by the invariant suite in
|
|
62
|
-
`libravdbd/compact/gate_test.go`.
|
|
63
|
-
|
|
64
|
-
If you introduce new signals or change weighting behavior, do not only update
|
|
65
|
-
the implementation. Add one of:
|
|
66
|
-
|
|
67
|
-
- a new invariant if the change alters a structural property of the gate
|
|
68
|
-
- a dedicated calibration/golden test file if the change adds new labeled
|
|
69
|
-
examples or expected decompositions
|
|
70
|
-
|
|
71
|
-
Do not rewrite expectations just to make regressions disappear.
|
|
25
|
+
If you change retrieval, compaction, or ranking behavior, add or update the
|
|
26
|
+
matching validation coverage and avoid weakening checks just to hide a
|
|
27
|
+
regression.
|
|
72
28
|
|
|
73
29
|
## PR Expectations
|
|
74
30
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
-
|
|
78
|
-
-
|
|
79
|
-
|
|
80
|
-
- any retrieval math or gating change must be reflected in the private design notes
|
|
81
|
-
|
|
82
|
-
## Release Versioning
|
|
83
|
-
|
|
84
|
-
`package.json` is the source of truth for the release version.
|
|
85
|
-
|
|
86
|
-
The release automation syncs `openclaw.plugin.json` from `package.json` during the
|
|
87
|
-
auto-bump/tag flow, and the publish workflow refuses to publish if the Git tag,
|
|
88
|
-
`package.json`, and `openclaw.plugin.json` versions do not all match.
|
|
31
|
+
- Keep plugin lifecycle and daemon lifecycle separate.
|
|
32
|
+
- Include focused docs updates for user-visible behavior or config changes.
|
|
33
|
+
- Keep internal design changes reflected in the appropriate design notes.
|
|
34
|
+
- Do not add install-time daemon bootstrap to the npm/OpenClaw package without
|
|
35
|
+
documenting the security and distribution trade-off.
|