@xdarkicex/openclaw-memory-libravdb 1.4.53 → 1.4.55

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 CHANGED
@@ -154,7 +154,7 @@ openclaw memory search "prior context"
154
154
  openclaw memory export --user-id <userId>
155
155
  openclaw memory flush --user-id <userId>
156
156
  openclaw memory journal --limit 50
157
- openclaw memory dream-promote --user-id <userId> --dream-file /path/to/DREAMS.md
157
+ openclaw memory dream-promote --user-id <userId> --dream-file ~/DREAMS.md
158
158
  ```
159
159
 
160
160
  Use [Install](./docs/install.md) for service lifecycle commands and
@@ -312,7 +312,10 @@ function escapeMemoryFactText(text) {
312
312
  .replaceAll("<", "&lt;")
313
313
  .replaceAll(">", "&gt;")
314
314
  .replaceAll('"', "&quot;")
315
- .replaceAll("'", "&#39;");
315
+ .replaceAll("'", "&#39;")
316
+ .replaceAll("\r", "&#13;")
317
+ .replaceAll("\n", "&#10;")
318
+ .replaceAll("\t", "&#9;");
316
319
  }
317
320
  function buildExactRecallFact(result, token) {
318
321
  const factText = extractExactRecallFactText(result.text, token);
@@ -1,5 +1,6 @@
1
1
  import fs from "node:fs";
2
2
  import fsp from "node:fs/promises";
3
+ import os from "node:os";
3
4
  import path from "node:path";
4
5
  import { getHashBackendName, hashBytes } from "./markdown-hash.js";
5
6
  import { formatError } from "./format-error.js";
@@ -10,9 +11,16 @@ const DEFAULT_MIN_UNIQUE_QUERIES = 2;
10
11
  const DREAM_PROMOTION_VERSION = 1;
11
12
  const DREAM_SOURCE_KIND = "dream";
12
13
  export function createDreamPromotionHandle(cfg, getRpc, logger = console, fsApi = createRealFsApi()) {
13
- const diaryPath = normalizeDiaryPath(cfg.dreamPromotionDiaryPath);
14
14
  const userId = cfg.dreamPromotionUserId?.trim() ?? "";
15
- if (cfg.dreamPromotionEnabled !== true || !diaryPath || !userId) {
15
+ if (cfg.dreamPromotionEnabled !== true || !userId) {
16
+ return {
17
+ async start() { },
18
+ async refresh() { },
19
+ async stop() { },
20
+ };
21
+ }
22
+ const diaryPath = normalizeDiaryPath(cfg.dreamPromotionDiaryPath);
23
+ if (!diaryPath) {
16
24
  return {
17
25
  async start() { },
18
26
  async refresh() { },
@@ -341,7 +349,26 @@ function normalizeDiaryPath(value) {
341
349
  if (!trimmed) {
342
350
  return "";
343
351
  }
344
- return path.resolve(trimmed);
352
+ // Reject traversal components — even though path.resolve collapses them,
353
+ // their presence signals an attempt to escape intended boundaries.
354
+ const segments = trimmed.split(/[/\\]+/);
355
+ if (segments.some((s) => s === "..")) {
356
+ throw new Error(`dream diary path must not contain ".." traversal: ${trimmed}`);
357
+ }
358
+ const resolved = path.resolve(trimmed);
359
+ // Restrict to known-safe locations to prevent arbitrary file reads.
360
+ // Allowed roots: home directory and the configured OpenClaw state dir.
361
+ const allowedRoots = [
362
+ os.homedir(),
363
+ process.env.OPENCLAW_STATE_DIR,
364
+ ]
365
+ .filter((r) => typeof r === "string" && r.trim().length > 0)
366
+ .map((root) => path.resolve(root));
367
+ const isAllowed = allowedRoots.some((root) => resolved.startsWith(root + path.sep) || resolved === root);
368
+ if (!isAllowed) {
369
+ throw new Error(`dream diary path must be within an allowed root: ${allowedRoots.join(", ")}. Got: ${resolved}`);
370
+ }
371
+ return resolved;
345
372
  }
346
373
  function createRealFsApi() {
347
374
  return {
package/dist/index.js CHANGED
@@ -9467,7 +9467,7 @@ var require_service_config = __commonJS({
9467
9467
  exports2.validateRetryThrottling = validateRetryThrottling;
9468
9468
  exports2.validateServiceConfig = validateServiceConfig;
9469
9469
  exports2.extractAndSelectServiceConfig = extractAndSelectServiceConfig;
9470
- var os2 = __require("os");
9470
+ var os3 = __require("os");
9471
9471
  var constants_1 = require_constants();
9472
9472
  var DURATION_REGEX = /^\d+(\.\d{1,9})?s$/;
9473
9473
  var CLIENT_LANGUAGE_STRING = "node";
@@ -9766,7 +9766,7 @@ var require_service_config = __commonJS({
9766
9766
  if (Array.isArray(validatedConfig.clientHostname)) {
9767
9767
  let hostnameMatched = false;
9768
9768
  for (const hostname2 of validatedConfig.clientHostname) {
9769
- if (hostname2 === os2.hostname()) {
9769
+ if (hostname2 === os3.hostname()) {
9770
9770
  hostnameMatched = true;
9771
9771
  }
9772
9772
  }
@@ -24066,7 +24066,7 @@ var require_subchannel_call = __commonJS({
24066
24066
  Object.defineProperty(exports2, "__esModule", { value: true });
24067
24067
  exports2.Http2SubchannelCall = void 0;
24068
24068
  var http2 = __require("http2");
24069
- var os2 = __require("os");
24069
+ var os3 = __require("os");
24070
24070
  var constants_1 = require_constants();
24071
24071
  var metadata_1 = require_metadata();
24072
24072
  var stream_decoder_1 = require_stream_decoder();
@@ -24074,7 +24074,7 @@ var require_subchannel_call = __commonJS({
24074
24074
  var constants_2 = require_constants();
24075
24075
  var TRACER_NAME = "subchannel_call";
24076
24076
  function getSystemErrorName(errno) {
24077
- for (const [name, num] of Object.entries(os2.constants.errno)) {
24077
+ for (const [name, num] of Object.entries(os3.constants.errno)) {
24078
24078
  if (num === errno) {
24079
24079
  return name;
24080
24080
  }
@@ -32292,6 +32292,7 @@ function formatError(error) {
32292
32292
  // src/dream-promotion.ts
32293
32293
  import fs from "node:fs";
32294
32294
  import fsp from "node:fs/promises";
32295
+ import os from "node:os";
32295
32296
  import path from "node:path";
32296
32297
 
32297
32298
  // src/markdown-hash.ts
@@ -32505,9 +32506,19 @@ var DEFAULT_DEBOUNCE_MS = 150;
32505
32506
  var DREAM_PROMOTION_VERSION = 1;
32506
32507
  var DREAM_SOURCE_KIND = "dream";
32507
32508
  function createDreamPromotionHandle(cfg, getRpc, logger = console, fsApi = createRealFsApi()) {
32508
- const diaryPath = normalizeDiaryPath(cfg.dreamPromotionDiaryPath);
32509
32509
  const userId = cfg.dreamPromotionUserId?.trim() ?? "";
32510
- if (cfg.dreamPromotionEnabled !== true || !diaryPath || !userId) {
32510
+ if (cfg.dreamPromotionEnabled !== true || !userId) {
32511
+ return {
32512
+ async start() {
32513
+ },
32514
+ async refresh() {
32515
+ },
32516
+ async stop() {
32517
+ }
32518
+ };
32519
+ }
32520
+ const diaryPath = normalizeDiaryPath(cfg.dreamPromotionDiaryPath);
32521
+ if (!diaryPath) {
32511
32522
  return {
32512
32523
  async start() {
32513
32524
  },
@@ -32836,7 +32847,26 @@ function normalizeDiaryPath(value) {
32836
32847
  if (!trimmed) {
32837
32848
  return "";
32838
32849
  }
32839
- return path.resolve(trimmed);
32850
+ const segments = trimmed.split(/[/\\]+/);
32851
+ if (segments.some((s) => s === "..")) {
32852
+ throw new Error(
32853
+ `dream diary path must not contain ".." traversal: ${trimmed}`
32854
+ );
32855
+ }
32856
+ const resolved = path.resolve(trimmed);
32857
+ const allowedRoots = [
32858
+ os.homedir(),
32859
+ process.env.OPENCLAW_STATE_DIR
32860
+ ].filter((r) => typeof r === "string" && r.trim().length > 0).map((root) => path.resolve(root));
32861
+ const isAllowed = allowedRoots.some(
32862
+ (root) => resolved.startsWith(root + path.sep) || resolved === root
32863
+ );
32864
+ if (!isAllowed) {
32865
+ throw new Error(
32866
+ `dream diary path must be within an allowed root: ${allowedRoots.join(", ")}. Got: ${resolved}`
32867
+ );
32868
+ }
32869
+ return resolved;
32840
32870
  }
32841
32871
  function createRealFsApi() {
32842
32872
  return {
@@ -33889,7 +33919,7 @@ function extractExactRecallFactText(text, token) {
33889
33919
  return factSentence ?? tail.split("\n")[0]?.trim() ?? tail;
33890
33920
  }
33891
33921
  function escapeMemoryFactText(text) {
33892
- return text.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
33922
+ return text.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;").replaceAll("\r", "&#13;").replaceAll("\n", "&#10;").replaceAll(" ", "&#9;");
33893
33923
  }
33894
33924
  function buildExactRecallFact(result, token) {
33895
33925
  const factText = extractExactRecallFactText(result.text, token);
@@ -39324,7 +39354,7 @@ var GrpcKernelClient = class {
39324
39354
  // src/sidecar.ts
39325
39355
  import fs3 from "node:fs";
39326
39356
  import net from "node:net";
39327
- import os from "node:os";
39357
+ import os2 from "node:os";
39328
39358
  import path4 from "node:path";
39329
39359
  var STARTUP_CONNECT_MAX_RETRIES = 5;
39330
39360
  var STARTUP_CONNECT_BASE_DELAY_MS = 100;
@@ -39566,7 +39596,7 @@ function resolveConfiguredEndpoint(cfg) {
39566
39596
  function daemonProvisioningHint() {
39567
39597
  return "If you installed the npm package, install and start libravdbd separately; the package does not provision the daemon binary, ONNX Runtime, or model assets.";
39568
39598
  }
39569
- function defaultEndpoint(platform = process.platform, homeDir = os.homedir(), pathExists = fs3.existsSync) {
39599
+ function defaultEndpoint(platform = process.platform, homeDir = os2.homedir(), pathExists = fs3.existsSync) {
39570
39600
  const envEndpoint = process.env.LIBRAVDB_RPC_ENDPOINT?.trim();
39571
39601
  if (envEndpoint && isConfiguredEndpoint(envEndpoint)) {
39572
39602
  return envEndpoint;
@@ -103,7 +103,7 @@ The plugin exposes `ingestionGateThreshold` for host-side gating decisions:
103
103
  | Key | Type | Default | Notes |
104
104
  |---|---|---|---|
105
105
  | `dreamPromotionEnabled` | boolean | `false` | Enable dream diary promotion |
106
- | `dreamPromotionDiaryPath` | string | — | Path to dream diary markdown file |
106
+ | `dreamPromotionDiaryPath` | string | — | Path to dream diary markdown file under the operator home directory or `OPENCLAW_STATE_DIR` |
107
107
  | `dreamPromotionUserId` | string | — | User ID for dream collection scoping |
108
108
  | `dreamPromotionDebounceMs` | number | `150` | Debounce window for dream diary changes |
109
109
 
package/docs/features.md CHANGED
@@ -105,11 +105,12 @@ Automatic diary watching:
105
105
  Manual run:
106
106
 
107
107
  ```bash
108
- openclaw memory dream-promote --user-id <userId> --dream-file /path/to/DREAMS.md
108
+ openclaw memory dream-promote --user-id <userId> --dream-file ~/DREAMS.md
109
109
  ```
110
110
 
111
- The manual command and watcher both use the same sidecar promotion RPC, so
112
- admission gates and provenance metadata are identical.
111
+ Dream diary files must live under the operator's home directory or the configured
112
+ `OPENCLAW_STATE_DIR`. The manual command and watcher both use the same sidecar
113
+ promotion RPC, so admission gates and provenance metadata are identical.
113
114
 
114
115
  ## Memory CLI
115
116
 
@@ -126,7 +127,7 @@ CLI API.
126
127
  | `openclaw memory flush --user-id <userId>` | Delete one durable user namespace after confirmation. |
127
128
  | `openclaw memory flush --session-key <sessionKey>` | Delete a namespace derived from a session key after confirmation. |
128
129
  | `openclaw memory journal --limit 50` | Inspect bounded lifecycle hints recorded by the sidecar. |
129
- | `openclaw memory dream-promote --user-id <userId> --dream-file <path>` | Promote vetted dream diary bullets. |
130
+ | `openclaw memory dream-promote --user-id <userId> --dream-file <path>` | Promote vetted dream diary bullets from a file under the operator home directory or `OPENCLAW_STATE_DIR`. |
130
131
 
131
132
  Use `--yes` with `flush` only when you intentionally want to skip the
132
133
  confirmation prompt.
@@ -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.4.53",
5
+ "version": "1.4.55",
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.4.53",
3
+ "version": "1.4.55",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",