@xdarkicex/openclaw-memory-libravdb 1.4.14 → 1.4.17

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.
@@ -30,15 +30,6 @@ function normalizeCompactResult(response) {
30
30
  },
31
31
  };
32
32
  }
33
- function describeUnexpectedContent(value) {
34
- try {
35
- const serialized = JSON.stringify(value);
36
- return serialized === undefined ? String(value) : serialized;
37
- }
38
- catch {
39
- return String(value);
40
- }
41
- }
42
33
  function stringifyKernelBlock(block) {
43
34
  if (!block || typeof block !== "object") {
44
35
  return "";
@@ -69,10 +60,6 @@ function stringifyKernelBlock(block) {
69
60
  case "image":
70
61
  return "[image omitted]";
71
62
  default:
72
- console.warn("[libravdb] unsupported kernel content block", {
73
- type: record.type,
74
- block: describeUnexpectedContent(record),
75
- });
76
63
  return typeof record.text === "string" ? record.text : "";
77
64
  }
78
65
  }
@@ -81,10 +68,6 @@ function normalizeKernelContent(content) {
81
68
  return content;
82
69
  }
83
70
  if (!Array.isArray(content)) {
84
- console.warn("[libravdb] unexpected kernel content shape", {
85
- kind: typeof content,
86
- value: describeUnexpectedContent(content),
87
- });
88
71
  return "";
89
72
  }
90
73
  return content.map(stringifyKernelBlock).filter((part) => part.length > 0).join("\n");
@@ -101,6 +84,14 @@ function approximateMessageTokens(message) {
101
84
  function approximateMessagesTokens(messages) {
102
85
  return messages.reduce((sum, message) => sum + approximateMessageTokens(message), 0);
103
86
  }
87
+ function normalizeCurrentTokenCount(currentTokenCount) {
88
+ if (typeof currentTokenCount !== "number" ||
89
+ !Number.isFinite(currentTokenCount) ||
90
+ currentTokenCount <= 0) {
91
+ return undefined;
92
+ }
93
+ return Math.max(1, Math.floor(currentTokenCount));
94
+ }
104
95
  function normalizeTokenBudget(tokenBudget) {
105
96
  if (typeof tokenBudget !== "number" || !Number.isFinite(tokenBudget) || tokenBudget <= 0) {
106
97
  return undefined;
@@ -128,6 +119,33 @@ function resolveDynamicCompactThreshold(tokenBudget, compactThreshold, compactio
128
119
  const fraction = normalizeThresholdFraction(compactionThresholdFraction);
129
120
  return Math.max(1, Math.floor(normalizedBudget * fraction));
130
121
  }
122
+ function resolvePredictiveCompactionTarget(params) {
123
+ const currentTokenCount = normalizeCurrentTokenCount(params.currentTokenCount);
124
+ const threshold = normalizeTokenBudget(params.threshold);
125
+ if (currentTokenCount == null || threshold == null || currentTokenCount < threshold) {
126
+ return undefined;
127
+ }
128
+ const belowThresholdTarget = Math.max(1, threshold - 1);
129
+ return belowThresholdTarget < currentTokenCount
130
+ ? belowThresholdTarget
131
+ : Math.max(1, currentTokenCount - 1);
132
+ }
133
+ function logPredictiveCompactionAttempt(params) {
134
+ params.logger.info?.(`LibraVDB predictive compaction trigger phase=${params.phase} sessionId=${params.sessionId} ` +
135
+ `currentTokenCount=${params.currentTokenCount} threshold=${params.threshold} ` +
136
+ `targetSize=${params.targetSize} tokenBudget=${params.tokenBudget ?? "unknown"}`);
137
+ }
138
+ function logPredictiveCompactionOutcome(params) {
139
+ const message = `LibraVDB predictive compaction ${params.compacted ? "completed" : "did not compact"} ` +
140
+ `phase=${params.phase} sessionId=${params.sessionId} currentTokenCount=${params.currentTokenCount} ` +
141
+ `threshold=${params.threshold} targetSize=${params.targetSize} tokenBudget=${params.tokenBudget ?? "unknown"}` +
142
+ (params.reason ? ` reason=${params.reason}` : "");
143
+ if (params.compacted) {
144
+ params.logger.info?.(message);
145
+ return;
146
+ }
147
+ params.logger.warn?.(message);
148
+ }
131
149
  function truncateContentToTokenBudget(content, tokenBudget) {
132
150
  if (tokenBudget <= 0)
133
151
  return "";
@@ -191,6 +209,10 @@ function buildBudgetFallbackContext(messages, tokenBudget) {
191
209
  systemPromptAddition: "",
192
210
  };
193
211
  }
212
+ function resolvePredictiveCompactionTokenCount(args) {
213
+ return (normalizeCurrentTokenCount(args.currentTokenCount) ??
214
+ approximateMessagesTokens(args.messages) + approximateTokenCount(args.prompt ?? ""));
215
+ }
194
216
  export function normalizeKernelMessage(message) {
195
217
  return {
196
218
  role: message.role,
@@ -263,7 +285,12 @@ export function buildContextEngineFactory(runtime, cfg, recallCache, logger = co
263
285
  sessionId: requireSessionId(args.sessionId, "compact"),
264
286
  force: args.force,
265
287
  ...(typeof targetSize === "number" ? { targetSize } : {}),
266
- ...(typeof args.currentTokenCount === "number" ? { currentTokenCount: args.currentTokenCount } : {}),
288
+ ...(() => {
289
+ const normalizedCurrentTokenCount = normalizeCurrentTokenCount(args.currentTokenCount);
290
+ return normalizedCurrentTokenCount != null
291
+ ? { currentTokenCount: normalizedCurrentTokenCount }
292
+ : {};
293
+ })(),
267
294
  ...(typeof cfg.continuityMinTurns === "number"
268
295
  ? { continuityMinTurns: cfg.continuityMinTurns }
269
296
  : {}),
@@ -293,6 +320,45 @@ export function buildContextEngineFactory(runtime, cfg, recallCache, logger = co
293
320
  };
294
321
  }
295
322
  }
323
+ async function performAfterTurnPredictiveCompaction(args) {
324
+ const dynamicCompactThreshold = getDynamicCompactThreshold(args.tokenBudget);
325
+ const predictiveTargetSize = resolvePredictiveCompactionTarget({
326
+ currentTokenCount: args.currentTokenCount,
327
+ threshold: dynamicCompactThreshold,
328
+ });
329
+ if (args.currentTokenCount == null ||
330
+ dynamicCompactThreshold == null ||
331
+ predictiveTargetSize == null) {
332
+ return;
333
+ }
334
+ logPredictiveCompactionAttempt({
335
+ logger,
336
+ phase: "afterTurn",
337
+ sessionId: args.sessionId,
338
+ currentTokenCount: args.currentTokenCount,
339
+ threshold: dynamicCompactThreshold,
340
+ targetSize: predictiveTargetSize,
341
+ tokenBudget: args.tokenBudget,
342
+ });
343
+ const compactionResult = await runCompaction({
344
+ sessionId: args.sessionId,
345
+ targetSize: predictiveTargetSize,
346
+ tokenBudget: args.tokenBudget,
347
+ force: true,
348
+ currentTokenCount: args.currentTokenCount,
349
+ });
350
+ logPredictiveCompactionOutcome({
351
+ logger,
352
+ phase: "afterTurn",
353
+ sessionId: args.sessionId,
354
+ currentTokenCount: args.currentTokenCount,
355
+ threshold: dynamicCompactThreshold,
356
+ targetSize: predictiveTargetSize,
357
+ tokenBudget: args.tokenBudget,
358
+ compacted: compactionResult.compacted,
359
+ reason: compactionResult.reason,
360
+ });
361
+ }
296
362
  return {
297
363
  info: { id: "libravdb-memory", name: "LibraVDB Memory", ownsCompaction: true },
298
364
  ownsCompaction: true,
@@ -337,18 +403,47 @@ export function buildContextEngineFactory(runtime, cfg, recallCache, logger = co
337
403
  },
338
404
  async assemble(args) {
339
405
  const messages = normalizeKernelMessages(args.messages);
340
- const currentContextTokens = approximateMessagesTokens(messages) + approximateTokenCount(args.prompt ?? "");
406
+ const currentContextTokens = resolvePredictiveCompactionTokenCount({
407
+ currentTokenCount: args.currentTokenCount,
408
+ messages,
409
+ prompt: args.prompt,
410
+ });
341
411
  const dynamicCompactThreshold = getDynamicCompactThreshold(args.tokenBudget);
342
- if (dynamicCompactThreshold != null &&
343
- currentContextTokens >= dynamicCompactThreshold) {
412
+ const predictiveTargetSize = resolvePredictiveCompactionTarget({
413
+ currentTokenCount: currentContextTokens,
414
+ threshold: dynamicCompactThreshold,
415
+ });
416
+ if (dynamicCompactThreshold != null && predictiveTargetSize != null) {
417
+ logPredictiveCompactionAttempt({
418
+ logger,
419
+ phase: "assemble",
420
+ sessionId: args.sessionId,
421
+ currentTokenCount: currentContextTokens,
422
+ threshold: dynamicCompactThreshold,
423
+ targetSize: predictiveTargetSize,
424
+ tokenBudget: args.tokenBudget,
425
+ });
344
426
  const compactionResult = await runCompaction({
345
427
  sessionId: args.sessionId,
428
+ targetSize: predictiveTargetSize,
346
429
  tokenBudget: args.tokenBudget,
347
430
  force: true,
348
431
  currentTokenCount: currentContextTokens,
349
432
  });
350
- if (!compactionResult.ok || !compactionResult.compacted) {
351
- logger.warn?.(`LibraVDB predictive compaction blocked assemble path at ${currentContextTokens} tokens (threshold=${dynamicCompactThreshold}): ${compactionResult.reason ?? "compaction declined"}`);
433
+ logPredictiveCompactionOutcome({
434
+ logger,
435
+ phase: "assemble",
436
+ sessionId: args.sessionId,
437
+ currentTokenCount: currentContextTokens,
438
+ threshold: dynamicCompactThreshold,
439
+ targetSize: predictiveTargetSize,
440
+ tokenBudget: args.tokenBudget,
441
+ compacted: compactionResult.compacted,
442
+ reason: compactionResult.reason,
443
+ });
444
+ if (!compactionResult.ok) {
445
+ logger.warn?.(`LibraVDB predictive compaction blocked assemble path at ${currentContextTokens} tokens ` +
446
+ `(threshold=${dynamicCompactThreshold}): ${compactionResult.reason ?? "compaction failed"}`);
352
447
  return buildBudgetFallbackContext(messages, args.tokenBudget);
353
448
  }
354
449
  }
@@ -396,8 +491,11 @@ export function buildContextEngineFactory(runtime, cfg, recallCache, logger = co
396
491
  async afterTurn(args) {
397
492
  const messages = normalizeKernelMessages(args.messages);
398
493
  const kernel = runtime.getKernel();
494
+ const currentTokenCount = normalizeCurrentTokenCount(typeof args.runtimeContext?.currentTokenCount === "number"
495
+ ? args.runtimeContext.currentTokenCount
496
+ : undefined);
399
497
  if (kernel) {
400
- return await kernel.afterTurn({
498
+ const result = await kernel.afterTurn({
401
499
  sessionId: args.sessionId,
402
500
  sessionKey: args.sessionKey,
403
501
  userId: args.userId,
@@ -405,12 +503,24 @@ export function buildContextEngineFactory(runtime, cfg, recallCache, logger = co
405
503
  prePromptMessageCount: args.prePromptMessageCount,
406
504
  isHeartbeat: args.isHeartbeat,
407
505
  });
506
+ await performAfterTurnPredictiveCompaction({
507
+ sessionId: args.sessionId,
508
+ tokenBudget: args.tokenBudget,
509
+ currentTokenCount,
510
+ });
511
+ return result;
408
512
  }
409
513
  const rpc = await runtime.getRpc();
410
- return await rpc.call("after_turn_kernel", {
514
+ const result = await rpc.call("after_turn_kernel", {
411
515
  ...args,
412
516
  messages,
413
517
  });
518
+ await performAfterTurnPredictiveCompaction({
519
+ sessionId: args.sessionId,
520
+ tokenBudget: args.tokenBudget,
521
+ currentTokenCount,
522
+ });
523
+ return result;
414
524
  }
415
525
  };
416
526
  }
package/dist/index.d.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  import { type OpenClawPluginApi } from "openclaw/plugin-sdk/plugin-entry";
2
+ export declare const MEMORY_ID = "libravdb-memory";
3
+ export declare function register(api: OpenClawPluginApi): void;
2
4
  declare const _default: {
3
5
  id: string;
4
6
  name: string;
package/dist/index.js CHANGED
@@ -8,33 +8,57 @@ import { buildMemoryPromptSection } from "./memory-provider.js";
8
8
  import { buildMemoryRuntimeBridge } from "./memory-runtime.js";
9
9
  import { createRecallCache } from "./recall-cache.js";
10
10
  import { createPluginRuntime } from "./plugin-runtime.js";
11
+ export const MEMORY_ID = "libravdb-memory";
12
+ export function register(api) {
13
+ const isFullMode = api.registrationMode === "full";
14
+ const cfg = api.pluginConfig;
15
+ // Null in non-full mode — cli.ts skips action handlers when runtime is null.
16
+ const runtimeOrNull = isFullMode
17
+ ? createPluginRuntime(cfg, api.logger ?? console)
18
+ : null;
19
+ registerMemoryCli(api, runtimeOrNull, cfg, api.logger ?? console);
20
+ if (!isFullMode)
21
+ return;
22
+ // TypeScript can't narrow through the ternary, so re-bind and guard.
23
+ const runtime = runtimeOrNull;
24
+ if (!runtime)
25
+ return; // unreachable but satisfies the type checker
26
+ const recallCache = createRecallCache();
27
+ // Exclusive slot check: refuse to register if another plugin owns the memory slot.
28
+ // plugins.slots.memory is the only configurable slot; context engine exclusivity
29
+ // is enforced by the registry at runtime (no config surface for it).
30
+ // "none" means memory is disabled — not a conflict, allow registration.
31
+ const memSlot = api.config?.plugins?.slots?.memory;
32
+ if (memSlot && memSlot !== MEMORY_ID && memSlot !== "none") {
33
+ throw new Error(`[libravdb-memory] plugins.slots.memory is "${memSlot}". ` +
34
+ `Set it to "libravdb-memory" before enabling this plugin.`);
35
+ }
36
+ // Migrated from three legacy calls to a single registerMemoryCapability.
37
+ api.registerMemoryCapability(MEMORY_ID, {
38
+ promptBuilder: buildMemoryPromptSection(runtime.getRpc, cfg, recallCache),
39
+ runtime: buildMemoryRuntimeBridge(runtime.getRpc, cfg),
40
+ });
41
+ api.registerContextEngine(MEMORY_ID, () => buildContextEngineFactory(runtime, cfg, recallCache, api.logger ?? console));
42
+ const markdownIngestion = createMarkdownIngestionHandle(cfg, runtime.getRpc, api.logger ?? console);
43
+ const dreamPromotion = createDreamPromotionHandle(cfg, runtime.getRpc, api.logger ?? console);
44
+ void markdownIngestion.start().catch((error) => {
45
+ api.logger?.warn?.(`LibraVDB markdown ingestion failed to start: ${error instanceof Error ? error.message : String(error)}`);
46
+ });
47
+ void dreamPromotion.start().catch((error) => {
48
+ api.logger?.warn?.(`LibraVDB dream promotion failed to start: ${error instanceof Error ? error.message : String(error)}`);
49
+ });
50
+ api.on("before_reset", createBeforeResetHook(runtime, api.logger ?? console));
51
+ api.on("session_end", createSessionEndHook(runtime, api.logger ?? console));
52
+ api.on("gateway_stop", async () => {
53
+ await dreamPromotion.stop();
54
+ await markdownIngestion.stop();
55
+ await runtime.shutdown();
56
+ });
57
+ }
11
58
  export default definePluginEntry({
12
- id: "libravdb-memory",
59
+ id: MEMORY_ID,
13
60
  name: "LibraVDB Memory",
14
61
  description: "Persistent vector memory with three-tier hybrid scoring",
15
62
  kind: ["memory", "context-engine"],
16
- register(api) {
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
- },
63
+ register,
40
64
  });
package/docs/README.md CHANGED
@@ -1,16 +1,31 @@
1
- # Documentation Index
2
-
3
- This index lists the public docs that remain in the main repo. Detailed math
4
- and private design notes are kept out of the public index on purpose.
5
-
6
- - [installation.md](./installation.md) - Complete install, activation, verification, and troubleshooting reference.
7
- - [install.md](./install.md) - Practical lifecycle guide for Homebrew, the OpenClaw plugin, and manual daemon management.
8
- - [uninstall.md](./uninstall.md) - Clean shutdown and removal guide for the plugin, daemon, and optional local data.
9
- - [architecture.md](./architecture.md) - Public system overview covering the plugin, daemon, storage, retrieval, and compaction flow.
10
- - [problem.md](./problem.md) - Technical argument for replacing the stock OpenClaw memory lifecycle in this use case.
11
- - [dependencies.md](./dependencies.md) - Why LibraVDB and slab-based storage were chosen for this plugin.
12
- - [models.md](./models.md) - ONNX model strategy, latency trade-offs, and shipped model roles.
13
- - [security.md](./security.md) - Security model, untrusted-memory framing, isolation guarantees, and deletion boundaries.
14
- - [contributing.md](./contributing.md) - Contributor workflow, prerequisites, and invariant test expectations.
15
- - [architecture-decisions/README.md](./architecture-decisions/README.md) - Index of the repository ADRs.
16
- - [embedding-profiles.md](./embedding-profiles.md) - Shipped embedding profile baseline and current profile metadata.
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>
@@ -1,88 +1,35 @@
1
1
  # Contributing
2
2
 
3
- ## Prerequisites
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
- - Node.js `>= 22`
6
- - `pnpm`
7
- - OpenClaw CLI for end-to-end plugin testing
7
+ ## Baseline Checks
8
8
 
9
- ## Core Validation Commands
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
- ## Local Daemon Build
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
- This prepares `.daemon-bin/libravdbd` for local plugin testing and copies locally available bundled assets into `.daemon-bin/`.
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
- ## Gating Invariants
46
-
47
- Do not weaken the gate invariants casually. The daemon-owned tests in `libravdbd/compact/gate_test.go` check structural properties:
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
- Before opening a PR:
76
-
77
- - `pnpm check` must pass
78
- - plugin integration coverage must pass against a running daemon or a prepared local daemon binary
79
- - any new gating signal must come with calibration or invariant coverage
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.
@@ -0,0 +1,98 @@
1
+ # Development
2
+
3
+ This document covers source setup and repository maintenance tasks. For user
4
+ installation, use [Install](./install.md).
5
+
6
+ ## Prerequisites
7
+
8
+ - Node.js `>= 22`
9
+ - `pnpm`
10
+ - OpenClaw CLI for end-to-end plugin testing
11
+ - a published or locally built `libravdbd` daemon for integration tests
12
+
13
+ Go is only required when building the daemon from a local daemon checkout or
14
+ regenerating Go gRPC stubs.
15
+
16
+ ## Source Setup
17
+
18
+ ```bash
19
+ pnpm install
20
+ pnpm check
21
+ ```
22
+
23
+ `pnpm check` runs TypeScript validation and unit tests:
24
+
25
+ ```bash
26
+ tsc --noEmit
27
+ tsc -p tsconfig.tests.json && node --test .ts-build/test/unit/*.test.js
28
+ ```
29
+
30
+ ## Local Daemon Build
31
+
32
+ Prepare `.daemon-bin/libravdbd` for local plugin testing:
33
+
34
+ ```bash
35
+ bash scripts/build-daemon.sh
36
+ ```
37
+
38
+ Supported inputs:
39
+
40
+ - installed daemon on `PATH`, such as `brew install libravdbd`
41
+ - `LIBRAVDBD_BINARY_PATH=/path/to/libravdbd`
42
+ - `LIBRAVDBD_SOURCE_DIR=/path/to/libravdbd` to build from a local daemon repo
43
+
44
+ For daemon-internal Go development and release work, use the separate
45
+ `libravdbd` repository.
46
+
47
+ ## Validation Commands
48
+
49
+ ```bash
50
+ pnpm check
51
+ npm run test:integration
52
+ ```
53
+
54
+ Benchmark and tuning commands are documented in
55
+ [Performance and tuning](./performance-and-tuning.md).
56
+
57
+ ## Generated IPC Files
58
+
59
+ The plugin imports generated IPC envelope and RPC payload classes from
60
+ `src/generated/libravdb/ipc/v1/rpc_pb.js`.
61
+
62
+ Those generated files are checked in and copied into `dist/generated/` during
63
+ `npm run build`:
64
+
65
+ ```bash
66
+ npm run build
67
+ ```
68
+
69
+ Do not replace those imports with the older external
70
+ `@xdarkicex/libravdb-contracts` path. The current package resolves generated
71
+ types from this repository.
72
+
73
+ ## Proto Generation
74
+
75
+ The repo also contains `api/proto/intelligence_kernel/v1/kernel.proto` and a
76
+ small `Makefile` target for Go gRPC stub generation:
77
+
78
+ ```bash
79
+ make proto
80
+ ```
81
+
82
+ That target assumes Homebrew-style locations for `go`, `protoc`, and the Go
83
+ plugins. Adjust the Makefile locally if your toolchain lives elsewhere.
84
+
85
+ ## Release Shape
86
+
87
+ The npm package contains:
88
+
89
+ - `README.md`
90
+ - `HOOK.md`
91
+ - `index.js`
92
+ - `openclaw.plugin.json`
93
+ - `package.json`
94
+ - `docs/`
95
+ - `dist/`
96
+
97
+ The package is connect-only. It does not compile Go code, download models, or
98
+ manage the daemon process during plugin installation.