@xdarkicex/openclaw-memory-libravdb 1.6.10 → 1.6.12

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.
@@ -42,4 +42,5 @@ export declare function promoteDreamDiaryFile(client: LibravDBClient, opts: {
42
42
  sourceMtimeMs?: number;
43
43
  }): Promise<DreamPromotionResult>;
44
44
  export declare function parseDreamPromotionCandidates(text: string): DreamPromotionCandidate[];
45
+ export declare function normalizeDiaryPath(value?: string): string;
45
46
  export {};
@@ -279,7 +279,7 @@ function parseHeading(value) {
279
279
  return normalizeSectionName(match[2] ?? "");
280
280
  }
281
281
  function isPromotionSection(section) {
282
- return section.includes("deep sleep") || section.includes("promot") || section.includes("dream");
282
+ return /^(?:deep sleep|dream promotion(?: candidates?)?|promotion candidates?|promote candidates?)$/.test(section);
283
283
  }
284
284
  function parseBulletCandidate(line) {
285
285
  const match = /^\s*[-*+]\s+(.+)$/.exec(line);
@@ -357,18 +357,23 @@ function parseInteger(value) {
357
357
  function normalizeSectionName(value) {
358
358
  return value.trim().toLowerCase().replace(/\s+/g, " ");
359
359
  }
360
- function normalizeDiaryPath(value) {
360
+ export function normalizeDiaryPath(value) {
361
361
  const trimmed = value?.trim();
362
362
  if (!trimmed) {
363
363
  return "";
364
364
  }
365
+ // Expand ~ to home directory before resolving. path.resolve does not
366
+ // expand tilde, so "~/dreams.md" would resolve to "<cwd>/~/dreams.md".
367
+ const expanded = trimmed.startsWith("~")
368
+ ? path.join(os.homedir(), trimmed.slice(1))
369
+ : trimmed;
365
370
  // Reject traversal components — even though path.resolve collapses them,
366
371
  // their presence signals an attempt to escape intended boundaries.
367
- const segments = trimmed.split(/[/\\]+/);
372
+ const segments = expanded.split(/[/\\]+/);
368
373
  if (segments.some((s) => s === "..")) {
369
374
  throw new Error(`dream diary path must not contain ".." traversal: ${trimmed}`);
370
375
  }
371
- const resolved = path.resolve(trimmed);
376
+ const resolved = path.resolve(expanded);
372
377
  // Restrict to known-safe locations to prevent arbitrary file reads.
373
378
  // Allowed roots: home directory and the configured OpenClaw state dir.
374
379
  const allowedRoots = [
package/dist/index.js CHANGED
@@ -25601,7 +25601,7 @@ function parseHeading(value) {
25601
25601
  return normalizeSectionName(match[2] ?? "");
25602
25602
  }
25603
25603
  function isPromotionSection(section) {
25604
- return section.includes("deep sleep") || section.includes("promot") || section.includes("dream");
25604
+ return /^(?:deep sleep|dream promotion(?: candidates?)?|promotion candidates?|promote candidates?)$/.test(section);
25605
25605
  }
25606
25606
  function parseBulletCandidate(line) {
25607
25607
  const match = /^\s*[-*+]\s+(.+)$/.exec(line);
@@ -25684,13 +25684,14 @@ function normalizeDiaryPath(value) {
25684
25684
  if (!trimmed) {
25685
25685
  return "";
25686
25686
  }
25687
- const segments = trimmed.split(/[/\\]+/);
25687
+ const expanded = trimmed.startsWith("~") ? path.join(os.homedir(), trimmed.slice(1)) : trimmed;
25688
+ const segments = expanded.split(/[/\\]+/);
25688
25689
  if (segments.some((s) => s === "..")) {
25689
25690
  throw new Error(
25690
25691
  `dream diary path must not contain ".." traversal: ${trimmed}`
25691
25692
  );
25692
25693
  }
25693
- const resolved = path.resolve(trimmed);
25694
+ const resolved = path.resolve(expanded);
25694
25695
  const allowedRoots = [
25695
25696
  os.homedir(),
25696
25697
  process.env.OPENCLAW_STATE_DIR
@@ -36796,14 +36797,15 @@ function extractHost(target) {
36796
36797
  const sep = withoutDns.lastIndexOf(":");
36797
36798
  return sep > 0 ? withoutDns.slice(0, sep) : withoutDns;
36798
36799
  }
36799
- function loadSecretFromEnv() {
36800
+ function loadSecretFromEnv(logger) {
36800
36801
  const secret = process.env.LIBRAVDB_AUTH_SECRET?.trim();
36801
36802
  if (secret) return secret;
36802
36803
  const secretPath = process.env.LIBRAVDB_AUTH_SECRET_FILE;
36803
36804
  if (secretPath) {
36804
36805
  try {
36805
36806
  return fs3.readFileSync(secretPath, "utf8").trim() || void 0;
36806
- } catch {
36807
+ } catch (error2) {
36808
+ logger?.warn?.(`LibraVDB: failed to read auth secret file "${secretPath}": ${formatError(error2)}`);
36807
36809
  return void 0;
36808
36810
  }
36809
36811
  }
@@ -1,5 +1,6 @@
1
1
  import type { Interceptor } from "@connectrpc/connect";
2
2
  import type { PartialMessage } from "@bufbuild/protobuf";
3
+ import type { LoggerLike } from "./types.js";
3
4
  import type { AfterTurnKernelRequest, AfterTurnKernelResponse, AssembleContextInternalRequest, AssembleContextInternalResponse, BootstrapSessionKernelRequest, BootstrapSessionKernelResponse, CompactSessionRequest, CompactSessionResponse, DeleteAuthoredDocumentRequest, DeleteAuthoredDocumentResponse, DreamPromotionResponse, ExportMemoryRequest, ExportMemoryResponse, FlushNamespaceRequest, FlushNamespaceResponse, FlushRequest, FlushResponse, HealthRequest, HealthResponse, IngestMarkdownDocumentRequest, IngestMarkdownDocumentResponse, IngestMessageKernelRequest, IngestMessageKernelResponse, ListCollectionRequest, ListCollectionResponse, ListLifecycleJournalRequest, ListLifecycleJournalResponse, MarkMemorySupersededRequest, MarkMemorySupersededResponse, MemoryStatusRequest, MemoryStatusResponse, PromoteDreamEntriesRequest, RankCandidatesRequest, RankCandidatesResponse, RebuildIndexRequest, RebuildIndexResponse, ReindexAuthoredDocumentRequest, ReindexAuthoredDocumentResponse, SearchTextCollectionsRequest, SearchTextRequest, SearchTextResponse, SessionLifecycleHintRequest, SessionLifecycleHintResponse } from "@xdarkicex/libravdb-contracts";
4
5
  export interface LibravDBClientOptions {
5
6
  endpoint?: string;
@@ -56,5 +57,5 @@ export declare class LibravDBClient {
56
57
  rankCandidates(req: PartialMessage<RankCandidatesRequest>): Promise<RankCandidatesResponse>;
57
58
  close(): void;
58
59
  }
59
- export declare function loadSecretFromEnv(): string | undefined;
60
+ export declare function loadSecretFromEnv(logger?: LoggerLike): string | undefined;
60
61
  export {};
@@ -3,6 +3,7 @@ import { createGrpcTransport } from "@connectrpc/connect-node";
3
3
  import { LibravDB } from "@xdarkicex/libravdb-contracts/client";
4
4
  import { createHmac } from "node:crypto";
5
5
  import fs from "node:fs";
6
+ import { formatError } from "./format-error.js";
6
7
  import net from "node:net";
7
8
  import os from "node:os";
8
9
  import path from "node:path";
@@ -279,7 +280,7 @@ function extractHost(target) {
279
280
  const sep = withoutDns.lastIndexOf(":");
280
281
  return sep > 0 ? withoutDns.slice(0, sep) : withoutDns;
281
282
  }
282
- export function loadSecretFromEnv() {
283
+ export function loadSecretFromEnv(logger) {
283
284
  const secret = process.env.LIBRAVDB_AUTH_SECRET?.trim();
284
285
  if (secret)
285
286
  return secret;
@@ -288,7 +289,8 @@ export function loadSecretFromEnv() {
288
289
  try {
289
290
  return fs.readFileSync(secretPath, "utf8").trim() || undefined;
290
291
  }
291
- catch {
292
+ catch (error) {
293
+ logger?.warn?.(`LibraVDB: failed to read auth secret file "${secretPath}": ${formatError(error)}`);
292
294
  return undefined;
293
295
  }
294
296
  }
@@ -15,6 +15,8 @@ CPU when a provider is unavailable.
15
15
  | `grpcEndpoint` | string | — | gRPC kernel endpoint. See `grpcEndpointTlsMode` for credential control. |
16
16
  | `grpcEndpointTlsCa` | string | — | Path to CA certificate PEM file. Only needed for self-signed or private CA certs. Omit when using Let's Encrypt or cert-manager. |
17
17
  | `grpcEndpointTlsMode` | string | `"auto"` | gRPC credential mode. `"auto"`: loopback/unix → plaintext, remote → TLS. `"tls"`: always TLS. `"insecure"`: always plaintext. |
18
+ | `grpcEndpointTlsClientCert` | string | — | Path to client certificate PEM for mTLS. Must be paired with `grpcEndpointTlsClientKey`. |
19
+ | `grpcEndpointTlsClientKey` | string | — | Path to client private key PEM for mTLS. Must be paired with `grpcEndpointTlsClientCert`. |
18
20
  | `rpcTimeoutMs` | number | `30000` | Per-call timeout for service RPC (ms) |
19
21
  | `dbPath` | string | auto-named | Explicit DB path; when set bypasses model-specific naming |
20
22
 
@@ -56,6 +58,19 @@ Set `grpcEndpointTlsCa` to the path of the CA certificate PEM file:
56
58
  The daemon must be configured with matching TLS cert and key via
57
59
  `LIBRAVDB_GRPC_TLS_CERT` and `LIBRAVDB_GRPC_TLS_KEY`.
58
60
 
61
+ **Remote daemon with mTLS (mutual TLS):**
62
+ When the daemon requires client certificate authentication, set both
63
+ `grpcEndpointTlsClientCert` and `grpcEndpointTlsClientKey` (they must both
64
+ be present or both be omitted):
65
+ ```json
66
+ {
67
+ "grpcEndpoint": "tcp:yourdaemon.internal:50051",
68
+ "grpcEndpointTlsCa": "/etc/certs/ca.pem",
69
+ "grpcEndpointTlsClientCert": "/etc/certs/client-cert.pem",
70
+ "grpcEndpointTlsClientKey": "/etc/certs/client-key.pem"
71
+ }
72
+ ```
73
+
59
74
  **Local daemon with TLS enabled:**
60
75
  If the daemon has `LIBRAVDB_GRPC_TLS_CERT`/`LIBRAVDB_GRPC_TLS_KEY` set on a loopback
61
76
  address, explicitly set `grpcEndpointTlsMode: "tls"` to match:
@@ -147,11 +162,49 @@ The plugin exposes `ingestionGateThreshold` for host-side gating decisions:
147
162
  | `markdownIngestionObsidianInclude` | string[] | — | Obsidian glob include patterns |
148
163
  | `markdownIngestionObsidianExclude` | string[] | same defaults as above | Obsidian glob exclude patterns; defaults to the same set as generic markdown ingestion |
149
164
  | `markdownIngestionObsidianDebounceMs` | number | `150` | Obsidian debounce window |
165
+ | `markdownIngestionSnapshotPath` | string | — | Path to snapshot file for generic markdown ingestion state |
166
+ | `markdownIngestionObsidianSnapshotPath` | string | — | Path to snapshot file for Obsidian ingestion state |
150
167
 
151
168
  Configured markdown roots are ignored unless the matching enable flag is set to
152
169
  `true`. Set `markdownIngestionEnabled: true` for generic roots and
153
170
  `markdownIngestionObsidianEnabled: true` for Obsidian vault roots.
154
171
 
172
+ ## Continuity
173
+
174
+ | Key | Type | Default | Notes |
175
+ |---|---|---|---|
176
+ | `continuityMinTurns` | number | — | Minimum conversation turns before continuity retrieval activates |
177
+ | `continuityPriorContextTokens` | number | — | Token budget allocated to prior context in continuity retrieval |
178
+ | `continuityTailBudgetTokens` | number | — | Token budget for tail-end context in continuity retrieval |
179
+
180
+ ## Recovery
181
+
182
+ | Key | Type | Default | Notes |
183
+ |---|---|---|---|
184
+ | `recoveryFloorScore` | number | — | Minimum score floor for recovery-phase retrieval |
185
+ | `recoveryMinConfidenceMean` | number | — | Minimum mean confidence threshold for recovery candidates |
186
+ | `recoveryMinTopK` | number | — | Minimum number of top-K results required for recovery |
187
+
188
+ ## Section 7 scoring
189
+
190
+ Two-pass retrieval scoring subsystem. These keys control the authority-weighted
191
+ and recency-adjusted scoring pass that runs after the initial vector search.
192
+
193
+ | Key | Type | Default | Notes |
194
+ |---|---|---|---|
195
+ | `section7Theta1` | number | — | Primary theta parameter for first-pass scoring |
196
+ | `section7Kappa` | number | — | Kappa scaling factor for authority scoring |
197
+ | `section7HopEta` | number | — | Eta decay for hop-distance scoring |
198
+ | `section7HopThreshold` | number | — | Hop distance threshold for second-pass eligibility |
199
+ | `section7CoarseTopK` | number | — | Top-K for coarse (first-pass) retrieval |
200
+ | `section7SecondPassTopK` | number | — | Top-K for second-pass refined retrieval |
201
+ | `section7AuthorityRecencyLambda` | number | — | Lambda for recency decay in authority scoring (commented out in code) |
202
+ | `section7AuthorityRecencyWeight` | number | — | Weight for authority recency in combined score |
203
+ | `section7AuthorityFrequencyWeight` | number | — | Weight for authority frequency in combined score |
204
+ | `section7AuthorityAuthoredWeight` | number | — | Weight for authority authored signal in combined score |
205
+ | `section7AuthoritySalienceWeight` | number | — | Weight for authority salience in combined score |
206
+ | `section7RecencyAccessLambda` | number | — | Lambda for access-based recency decay |
207
+
155
208
  ## Dream promotion
156
209
 
157
210
  | Key | Type | Default | Notes |
@@ -35,6 +35,34 @@ How it works:
35
35
  - The sidecar store persists an embedding fingerprint, so reopening an existing store with a different effective model profile will fail instead of silently mixing vector spaces.
36
36
  - `onnxDevice` is passed through as `LIBRAVDB_ONNX_DEVICE` for daemon versions that support execution-provider selection (`auto`, `cpu`, `cuda`, `coreml`, `directml`, `openvino`).
37
37
 
38
+ ## Store Compatibility and Upgrades
39
+
40
+ The persisted embedding fingerprint is part of the database compatibility check.
41
+ That is intentional: if a daemon opens a store with a different effective model
42
+ profile, the safest outcome is to stop before mixing vector spaces.
43
+
44
+ When updating `libravdbd`, keep the same effective embedding profile unless you
45
+ intend to rebuild the store. The effective profile includes the profile family,
46
+ dimensions, normalization setting, and any metadata supplied by the model
47
+ manifest or daemon defaults.
48
+
49
+ Legacy local setups can be more fragile than the current packaged profiles. For
50
+ example, older `all-minilm-l6-v2` stores may fail to reopen after a daemon update
51
+ if the daemon now computes different metadata for that local profile. This does
52
+ not imply that current packaged `nomic-embed-text-v1.5` or
53
+ `bge-small-en-v1.5` stores are incompatible; it means the old local profile must
54
+ be treated as a separate vector space unless a migration path is provided.
55
+
56
+ If a daemon update reports that the database format or embedding profile is
57
+ incompatible:
58
+
59
+ 1. back up both the `.libravdb` file and its `.embedding.json` metadata file;
60
+ 2. either downgrade to the previous daemon that created the store, or move the
61
+ old store aside and let the new daemon initialize a fresh database;
62
+ 3. rebuild/reingest memories with the new effective embedding profile.
63
+
64
+ Do not delete the old store until the replacement has been verified.
65
+
38
66
  Recommended usage:
39
67
 
40
68
  - `bundled` for the shipped default path, which uses `nomic-embed-text-v1.5`.
@@ -171,6 +171,30 @@ For foreground debugging:
171
171
  libravdbd serve
172
172
  ```
173
173
 
174
+ ### Incompatible database or embedding profile
175
+
176
+ If the daemon exits with `database format is incompatible` or `database
177
+ embedding profile is incompatible`, it is refusing to open a store whose saved
178
+ format or embedding fingerprint differs from the current daemon settings. This
179
+ fail-closed behavior protects the store from mixing incompatible vector spaces.
180
+
181
+ Before changing anything, back up both files for the affected store:
182
+
183
+ - the database file, such as `$HOME/.libravdbd/data_nomic-embed-text-v1_5.libravdb`
184
+ - the adjacent `.embedding.json` metadata file
185
+
186
+ Then choose one recovery path:
187
+
188
+ - downgrade to the daemon version that created the store; or
189
+ - move the old store aside, start the new daemon so it creates a fresh store,
190
+ and rebuild/reingest memories with the current embedding profile.
191
+
192
+ This can affect legacy local profiles such as older `all-minilm-l6-v2` setups
193
+ when daemon defaults or model metadata change across releases. It is not
194
+ expected for stores that stay on the current packaged profiles and assets. See
195
+ [Embedding Profiles](./embedding-profiles.md#store-compatibility-and-upgrades)
196
+ for more detail.
197
+
174
198
  ### Hash mismatch
175
199
 
176
200
  Do not bypass a checksum mismatch. Delete the corrupt or stale asset and rerun
@@ -2,7 +2,7 @@
2
2
  "id": "libravdb-memory",
3
3
  "name": "LibraVDB Memory",
4
4
  "description": "Persistent vector memory with three-tier hybrid scoring",
5
- "version": "1.6.10",
5
+ "version": "1.6.12",
6
6
  "kind": [
7
7
  "memory",
8
8
  "context-engine"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xdarkicex/openclaw-memory-libravdb",
3
- "version": "1.6.10",
3
+ "version": "1.6.12",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",