@xdarkicex/openclaw-memory-libravdb 1.4.9 → 1.4.11

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.
@@ -1,6 +1,42 @@
1
1
  import type { PluginRuntime } from "./plugin-runtime.js";
2
2
  import type { LoggerLike, PluginConfig, RecallCache, SearchResult } from "./types.js";
3
3
  import { AssembleContextInternalResponse } from "./generated/libravdb/ipc/v1/rpc_pb.js";
4
+ type KernelCompatibleMessage = {
5
+ role: string;
6
+ content: string;
7
+ id?: string;
8
+ };
9
+ type OpenClawCompatibleMessage = {
10
+ role: string;
11
+ content: string;
12
+ id?: string;
13
+ };
14
+ type OpenClawCompatibleAssembleResult = {
15
+ messages: OpenClawCompatibleMessage[];
16
+ estimatedTokens: number;
17
+ systemPromptAddition: string;
18
+ debug?: AssembleContextInternalResponse["debug"];
19
+ };
20
+ export declare function normalizeKernelMessage(message: {
21
+ role: string;
22
+ content: unknown;
23
+ id?: string;
24
+ }): KernelCompatibleMessage;
25
+ export declare function normalizeKernelMessages(messages: Array<{
26
+ role: string;
27
+ content: unknown;
28
+ id?: string;
29
+ }>): KernelCompatibleMessage[];
30
+ export declare function normalizeAssembleResult(result: {
31
+ messages?: Array<{
32
+ role: string;
33
+ content?: unknown;
34
+ id?: string;
35
+ }>;
36
+ estimatedTokens?: number;
37
+ systemPromptAddition?: string;
38
+ debug?: AssembleContextInternalResponse["debug"];
39
+ }): OpenClawCompatibleAssembleResult;
4
40
  export declare function buildContextEngineFactory(runtime: PluginRuntime, cfg: PluginConfig, recallCache: RecallCache<SearchResult>, logger?: LoggerLike): {
5
41
  info: {
6
42
  id: string;
@@ -19,7 +55,7 @@ export declare function buildContextEngineFactory(runtime: PluginRuntime, cfg: P
19
55
  userId?: string;
20
56
  message: {
21
57
  role: string;
22
- content: string;
58
+ content: unknown;
23
59
  id?: string;
24
60
  };
25
61
  isHeartbeat?: boolean;
@@ -30,16 +66,17 @@ export declare function buildContextEngineFactory(runtime: PluginRuntime, cfg: P
30
66
  userId?: string;
31
67
  messages: Array<{
32
68
  role: string;
33
- content: string;
69
+ content: unknown;
34
70
  id?: string;
35
71
  }>;
36
72
  tokenBudget: number;
37
73
  prompt?: string;
38
- }): Promise<AssembleContextInternalResponse>;
74
+ }): Promise<OpenClawCompatibleAssembleResult>;
39
75
  compact(args: {
40
76
  sessionId: string;
41
77
  force?: boolean;
42
78
  targetSize?: number;
79
+ tokenBudget?: number;
43
80
  }): Promise<any>;
44
81
  afterTurn(args: {
45
82
  sessionId: string;
@@ -47,10 +84,13 @@ export declare function buildContextEngineFactory(runtime: PluginRuntime, cfg: P
47
84
  userId?: string;
48
85
  messages: Array<{
49
86
  role: string;
50
- content: string;
87
+ content: unknown;
51
88
  id?: string;
52
89
  }>;
53
90
  prePromptMessageCount?: number;
54
91
  isHeartbeat?: boolean;
92
+ tokenBudget?: number;
93
+ runtimeContext?: Record<string, unknown>;
55
94
  }): Promise<any>;
56
95
  };
96
+ export {};
@@ -1,4 +1,113 @@
1
+ function describeUnexpectedContent(value) {
2
+ try {
3
+ const serialized = JSON.stringify(value);
4
+ return serialized === undefined ? String(value) : serialized;
5
+ }
6
+ catch {
7
+ return String(value);
8
+ }
9
+ }
10
+ function stringifyKernelBlock(block) {
11
+ if (!block || typeof block !== "object") {
12
+ return "";
13
+ }
14
+ const record = block;
15
+ switch (record.type) {
16
+ case "text":
17
+ return typeof record.text === "string" ? record.text : "";
18
+ case "thinking":
19
+ return typeof record.thinking === "string" ? record.thinking : "";
20
+ case "toolCall": {
21
+ const name = typeof record.name === "string" ? record.name : "tool";
22
+ const args = record.arguments;
23
+ let renderedArgs = "";
24
+ if (typeof args === "string") {
25
+ renderedArgs = args;
26
+ }
27
+ else if (args !== undefined) {
28
+ try {
29
+ renderedArgs = JSON.stringify(args);
30
+ }
31
+ catch {
32
+ renderedArgs = String(args);
33
+ }
34
+ }
35
+ return renderedArgs ? `[tool:${name}] ${renderedArgs}` : `[tool:${name}]`;
36
+ }
37
+ case "image":
38
+ return "[image omitted]";
39
+ default:
40
+ console.warn("[libravdb] unsupported kernel content block", {
41
+ type: record.type,
42
+ block: describeUnexpectedContent(record),
43
+ });
44
+ return typeof record.text === "string" ? record.text : "";
45
+ }
46
+ }
47
+ function normalizeKernelContent(content) {
48
+ if (typeof content === "string") {
49
+ return content;
50
+ }
51
+ if (!Array.isArray(content)) {
52
+ console.warn("[libravdb] unexpected kernel content shape", {
53
+ kind: typeof content,
54
+ value: describeUnexpectedContent(content),
55
+ });
56
+ return "";
57
+ }
58
+ return content.map(stringifyKernelBlock).filter((part) => part.length > 0).join("\n");
59
+ }
60
+ export function normalizeKernelMessage(message) {
61
+ return {
62
+ role: message.role,
63
+ content: normalizeKernelContent(message.content),
64
+ ...(typeof message.id === "string" ? { id: message.id } : {}),
65
+ };
66
+ }
67
+ export function normalizeKernelMessages(messages) {
68
+ return messages.map((message) => normalizeKernelMessage(message));
69
+ }
70
+ export function normalizeAssembleResult(result) {
71
+ const messages = Array.isArray(result.messages)
72
+ ? result.messages.map((message) => ({
73
+ // OpenClaw replay only expects conversational turns here, so assemble output
74
+ // is collapsed to user/assistant even though normalizeKernelMessage preserves
75
+ // richer inbound roles. If kernel.assembleContext starts emitting other roles,
76
+ // this coercion point is where that contract needs to be revisited.
77
+ role: message.role === "user" ? "user" : "assistant",
78
+ content: normalizeKernelContent(message.content),
79
+ ...(typeof message.id === "string" ? { id: message.id } : {}),
80
+ }))
81
+ : [];
82
+ return {
83
+ messages,
84
+ estimatedTokens: typeof result.estimatedTokens === "number" ? result.estimatedTokens : 0,
85
+ systemPromptAddition: typeof result.systemPromptAddition === "string" ? result.systemPromptAddition : "",
86
+ ...(result.debug != null ? { debug: result.debug } : {}),
87
+ };
88
+ }
1
89
  export function buildContextEngineFactory(runtime, cfg, recallCache, logger = console) {
90
+ function buildCompactSessionRequest(args) {
91
+ // OpenClaw core now requests budget-style compaction using tokenBudget,
92
+ // but the current LibraVDB compact_session wire contract still expects
93
+ // targetSize. Use tokenBudget as the compatibility target so overflow and
94
+ // timeout retries still compact toward the host's requested prompt budget.
95
+ const targetSize = args.targetSize ?? args.tokenBudget;
96
+ return {
97
+ sessionId: args.sessionId,
98
+ force: args.force,
99
+ ...(typeof targetSize === "number" ? { targetSize } : {}),
100
+ ...(typeof cfg.continuityMinTurns === "number"
101
+ ? { continuityMinTurns: cfg.continuityMinTurns }
102
+ : {}),
103
+ ...(typeof cfg.continuityTailBudgetTokens === "number"
104
+ ? { continuityTailBudgetTokens: cfg.continuityTailBudgetTokens }
105
+ : {}),
106
+ ...(typeof cfg.continuityPriorContextTokens === "number"
107
+ ? { continuityPriorContextTokens: cfg.continuityPriorContextTokens }
108
+ : {}),
109
+ };
110
+ }
2
111
  return {
3
112
  info: { id: "libravdb-memory", name: "LibraVDB Memory", ownsCompaction: true },
4
113
  ownsCompaction: true,
@@ -24,39 +133,44 @@ export function buildContextEngineFactory(runtime, cfg, recallCache, logger = co
24
133
  return await rpc.call("bootstrap_session_kernel", args);
25
134
  },
26
135
  async ingest(args) {
136
+ const message = normalizeKernelMessage(args.message);
27
137
  const kernel = runtime.getKernel();
28
138
  if (kernel) {
29
139
  return await kernel.ingestMessage({
30
140
  sessionId: args.sessionId,
31
141
  sessionKey: args.sessionKey,
32
142
  userId: args.userId,
33
- message: args.message,
143
+ message,
34
144
  isHeartbeat: args.isHeartbeat,
35
145
  });
36
146
  }
37
147
  const rpc = await runtime.getRpc();
38
- return await rpc.call("ingest_message_kernel", args);
148
+ return await rpc.call("ingest_message_kernel", {
149
+ ...args,
150
+ message,
151
+ });
39
152
  },
40
153
  async assemble(args) {
154
+ const messages = normalizeKernelMessages(args.messages);
41
155
  const kernel = runtime.getKernel();
42
156
  if (kernel) {
43
- return await kernel.assembleContext({
157
+ return normalizeAssembleResult(await kernel.assembleContext({
44
158
  sessionId: args.sessionId,
45
159
  sessionKey: args.sessionKey,
46
160
  userId: args.userId,
47
161
  queryText: args.prompt ?? "",
48
- visibleMessages: args.messages,
162
+ visibleMessages: messages,
49
163
  tokenBudget: args.tokenBudget,
50
164
  config: {},
51
165
  emitDebug: true
52
- });
166
+ }));
53
167
  }
54
168
  const rpc = await runtime.getRpc();
55
169
  const resp = await rpc.call("assemble_context_internal", {
56
170
  sessionId: args.sessionId,
57
171
  sessionKey: args.sessionKey,
58
172
  userId: args.userId,
59
- messages: args.messages,
173
+ messages,
60
174
  tokenBudget: args.tokenBudget,
61
175
  prompt: args.prompt,
62
176
  emitDebug: true,
@@ -92,34 +206,35 @@ export function buildContextEngineFactory(runtime, cfg, recallCache, logger = co
92
206
  ingestionGateThreshold: cfg.ingestionGateThreshold,
93
207
  },
94
208
  });
95
- return resp;
209
+ return normalizeAssembleResult(resp);
96
210
  },
97
211
  async compact(args) {
212
+ const request = buildCompactSessionRequest(args);
98
213
  const kernel = runtime.getKernel();
99
214
  if (kernel) {
100
- return await kernel.compactSession({
101
- sessionId: args.sessionId,
102
- force: args.force,
103
- targetSize: args.targetSize,
104
- });
215
+ return await kernel.compactSession(request);
105
216
  }
106
217
  const rpc = await runtime.getRpc();
107
- return await rpc.call("compact_session", args);
218
+ return await rpc.call("compact_session", request);
108
219
  },
109
220
  async afterTurn(args) {
221
+ const messages = normalizeKernelMessages(args.messages);
110
222
  const kernel = runtime.getKernel();
111
223
  if (kernel) {
112
224
  return await kernel.afterTurn({
113
225
  sessionId: args.sessionId,
114
226
  sessionKey: args.sessionKey,
115
227
  userId: args.userId,
116
- messages: args.messages,
228
+ messages,
117
229
  prePromptMessageCount: args.prePromptMessageCount,
118
230
  isHeartbeat: args.isHeartbeat,
119
231
  });
120
232
  }
121
233
  const rpc = await runtime.getRpc();
122
- return await rpc.call("after_turn_kernel", args);
234
+ return await rpc.call("after_turn_kernel", {
235
+ ...args,
236
+ messages,
237
+ });
123
238
  }
124
239
  };
125
240
  }
package/dist/index.js CHANGED
@@ -12,7 +12,7 @@ export default definePluginEntry({
12
12
  id: "libravdb-memory",
13
13
  name: "LibraVDB Memory",
14
14
  description: "Persistent vector memory with three-tier hybrid scoring",
15
- kind: "context-engine",
15
+ kind: ["memory", "context-engine"],
16
16
  register(api) {
17
17
  const cfg = api.pluginConfig;
18
18
  const recallCache = createRecallCache();
@@ -4,6 +4,7 @@ import type { LoggerLike, PluginConfig } from "./types.js";
4
4
  export type RpcGetter = () => Promise<RpcClient>;
5
5
  export declare const DEFAULT_RPC_TIMEOUT_MS = 30000;
6
6
  export declare const STARTUP_HEALTH_TIMEOUT_MS = 2000;
7
+ export declare function resolveStartupHealthTimeoutMs(cfg: PluginConfig): number;
7
8
  export interface LifecycleHint {
8
9
  hook: "before_reset" | "session_end";
9
10
  reason?: string;
@@ -4,6 +4,9 @@ import { daemonProvisioningHint, startSidecar } from "./sidecar.js";
4
4
  import { readFileSync } from "node:fs";
5
5
  export const DEFAULT_RPC_TIMEOUT_MS = 30000;
6
6
  export const STARTUP_HEALTH_TIMEOUT_MS = 2000;
7
+ export function resolveStartupHealthTimeoutMs(cfg) {
8
+ return Math.max(STARTUP_HEALTH_TIMEOUT_MS, cfg.rpcTimeoutMs ?? DEFAULT_RPC_TIMEOUT_MS);
9
+ }
7
10
  export function createPluginRuntime(cfg, logger = console) {
8
11
  let started = null;
9
12
  let stopped = false;
@@ -19,7 +22,7 @@ export function createPluginRuntime(cfg, logger = console) {
19
22
  timeoutMs: cfg.rpcTimeoutMs ?? DEFAULT_RPC_TIMEOUT_MS,
20
23
  });
21
24
  const health = await rpc.call("health", {}, {
22
- timeoutMs: STARTUP_HEALTH_TIMEOUT_MS,
25
+ timeoutMs: resolveStartupHealthTimeoutMs(cfg),
23
26
  });
24
27
  if (!health.ok) {
25
28
  try {
@@ -23,6 +23,13 @@ function normalizeSearchTextResponse(bytes) {
23
23
  }
24
24
  return response;
25
25
  }
26
+ function normalizeAssembleContextInternalResponse(bytes) {
27
+ const response = decodeProtobufResult(AssembleContextInternalResponse, bytes);
28
+ if (!Array.isArray(response.messages)) {
29
+ response.messages = [];
30
+ }
31
+ return response;
32
+ }
26
33
  function normalizeExcludeByCollection(value) {
27
34
  const normalized = {};
28
35
  if (!value) {
@@ -68,7 +75,7 @@ export const rpcProtobufCodecs = {
68
75
  bootstrap_session_kernel: codec((params) => encodeMessage(BootstrapSessionKernelRequest, params), (bytes) => decodeProtobufResult(BootstrapSessionKernelResponse, bytes)),
69
76
  ingest_message_kernel: codec((params) => encodeMessage(IngestMessageKernelRequest, params), (bytes) => decodeProtobufResult(IngestMessageKernelResponse, bytes)),
70
77
  after_turn_kernel: codec((params) => encodeMessage(AfterTurnKernelRequest, params), (bytes) => decodeProtobufResult(AfterTurnKernelResponse, bytes)),
71
- assemble_context_internal: codec((params) => encodeMessage(AssembleContextInternalRequest, params), (bytes) => decodeProtobufResult(AssembleContextInternalResponse, bytes)),
78
+ assemble_context_internal: codec((params) => encodeMessage(AssembleContextInternalRequest, params), normalizeAssembleContextInternalResponse),
72
79
  compact_session: codec((params) => encodeMessage(CompactSessionRequest, params), (bytes) => decodeProtobufResult(CompactSessionResponse, bytes)),
73
80
  rank_candidates: codec((params) => encodeMessage(RankCandidatesRequest, params), (bytes) => decodeProtobufResult(RankCandidatesResponse, bytes)),
74
81
  };
package/dist/rpc.d.ts CHANGED
@@ -8,6 +8,7 @@ export declare class RpcClient {
8
8
  private sentMagic;
9
9
  constructor(socket: SidecarSocket, options: RpcCallOptions);
10
10
  call<T>(method: string, params: unknown, callOptions?: Partial<RpcCallOptions>): Promise<T>;
11
+ private waitForReconnect;
11
12
  private handleData;
12
13
  private dispatchMessage;
13
14
  private rejectAll;
package/dist/rpc.js CHANGED
@@ -26,12 +26,13 @@ export class RpcClient {
26
26
  return await new Promise((resolve, reject) => {
27
27
  const id = ++this.seq;
28
28
  const timeoutMs = callOptions.timeoutMs ?? this.options.timeoutMs;
29
+ const deadline = Date.now() + timeoutMs;
29
30
  const timer = setTimeout(() => {
30
31
  this.pending.delete(id);
31
32
  reject(new Error(`RPC timeout: ${method} (${timeoutMs}ms)`));
32
33
  }, timeoutMs);
33
34
  this.pending.set(id, { resolve, reject, timer, decodeResult: codec.decodeResult });
34
- try {
35
+ const buildFrame = () => {
35
36
  const envelope = new RpcRequest({
36
37
  id,
37
38
  method,
@@ -41,18 +42,93 @@ export class RpcClient {
41
42
  const header = Buffer.alloc(4);
42
43
  header.writeUInt32BE(payload.byteLength, 0);
43
44
  const chunks = [];
44
- if (!this.sentMagic) {
45
+ const includesMagic = !this.sentMagic;
46
+ if (includesMagic) {
45
47
  chunks.push(Buffer.from([0x02]));
46
- this.sentMagic = true;
47
48
  }
48
49
  chunks.push(header, payload);
49
- this.socket.write(Buffer.concat(chunks));
50
- }
51
- catch (error) {
50
+ return { frame: Buffer.concat(chunks), includesMagic };
51
+ };
52
+ const send = (allowReconnectRetry) => {
53
+ if (!this.pending.has(id)) {
54
+ return;
55
+ }
56
+ const { frame, includesMagic } = buildFrame();
57
+ try {
58
+ this.socket.write(frame);
59
+ if (includesMagic) {
60
+ this.sentMagic = true;
61
+ }
62
+ }
63
+ catch (error) {
64
+ if (allowReconnectRetry && isReconnectableSocketGap(error)) {
65
+ this.sentMagic = false;
66
+ const remainingMs = Math.max(0, deadline - Date.now());
67
+ if (remainingMs <= 0) {
68
+ if (!this.pending.has(id)) {
69
+ return;
70
+ }
71
+ clearTimeout(timer);
72
+ this.pending.delete(id);
73
+ reject(new Error(`RPC timeout: ${method} (${timeoutMs}ms)`));
74
+ return;
75
+ }
76
+ void this.waitForReconnect(remainingMs)
77
+ .then(() => {
78
+ if (!this.pending.has(id)) {
79
+ return;
80
+ }
81
+ send(false);
82
+ })
83
+ .catch((reconnectError) => {
84
+ if (!this.pending.has(id)) {
85
+ return;
86
+ }
87
+ clearTimeout(timer);
88
+ this.pending.delete(id);
89
+ reject(reconnectError instanceof Error
90
+ ? reconnectError
91
+ : new Error(String(reconnectError)));
92
+ });
93
+ return;
94
+ }
95
+ clearTimeout(timer);
96
+ this.pending.delete(id);
97
+ reject(error instanceof Error ? error : new Error(String(error)));
98
+ }
99
+ };
100
+ send(true);
101
+ });
102
+ }
103
+ async waitForReconnect(timeoutMs) {
104
+ await new Promise((resolve, reject) => {
105
+ let settled = false;
106
+ const onConnect = () => {
107
+ if (settled)
108
+ return;
109
+ settled = true;
52
110
  clearTimeout(timer);
53
- this.pending.delete(id);
54
- reject(error instanceof Error ? error : new Error(String(error)));
55
- }
111
+ this.socket.off("error", onError);
112
+ resolve();
113
+ };
114
+ const onError = (error) => {
115
+ if (settled)
116
+ return;
117
+ settled = true;
118
+ clearTimeout(timer);
119
+ this.socket.off("connect", onConnect);
120
+ reject(error);
121
+ };
122
+ const timer = setTimeout(() => {
123
+ if (settled)
124
+ return;
125
+ settled = true;
126
+ this.socket.off("connect", onConnect);
127
+ this.socket.off("error", onError);
128
+ reject(new Error(`Sidecar reconnect timed out (${timeoutMs}ms)`));
129
+ }, timeoutMs);
130
+ this.socket.once("connect", onConnect);
131
+ this.socket.once("error", onError);
56
132
  });
57
133
  }
58
134
  handleData(chunk) {
@@ -119,3 +195,6 @@ export class RpcClient {
119
195
  }
120
196
  }
121
197
  }
198
+ function isReconnectableSocketGap(error) {
199
+ return error instanceof Error && /Sidecar socket unavailable/i.test(error.message);
200
+ }
package/dist/sidecar.d.ts CHANGED
@@ -17,6 +17,7 @@ declare class PlaceholderSocket implements SidecarSocket {
17
17
  setEncoding(_encoding: string): void;
18
18
  on(event: "data" | "close" | "error", handler: DataHandler | CloseHandler | ErrorHandler): void;
19
19
  once(event: "connect" | "error", handler: CloseHandler | ErrorHandler): void;
20
+ off(event: "connect" | "error", handler: CloseHandler | ErrorHandler): void;
20
21
  write(chunk: Buffer | string): void;
21
22
  destroy(): void;
22
23
  private emitError;
@@ -28,7 +29,7 @@ export declare function isTcpEndpoint(endpoint: string): boolean;
28
29
  export declare function resolveEndpoint(cfg: PluginConfig): string;
29
30
  export declare function resolveConfiguredEndpoint(cfg: PluginConfig): string;
30
31
  export declare function daemonProvisioningHint(): string;
31
- export declare function defaultEndpoint(platform?: NodeJS.Platform, homeDir?: string): string;
32
+ export declare function defaultEndpoint(platform?: NodeJS.Platform, homeDir?: string, pathExists?: (path: string) => boolean): string;
32
33
  export declare function buildSidecarEnv(cfg: PluginConfig): Record<string, string>;
33
34
  export { PlaceholderSocket };
34
35
  export declare function probeSidecarEndpoint(cfg: PluginConfig): Promise<string | null>;
package/dist/sidecar.js CHANGED
@@ -38,6 +38,13 @@ class PlaceholderSocket {
38
38
  }
39
39
  this.errorOnce.add(handler);
40
40
  }
41
+ off(event, handler) {
42
+ if (event === "connect") {
43
+ this.connectOnce.delete(handler);
44
+ return;
45
+ }
46
+ this.errorOnce.delete(handler);
47
+ }
41
48
  write(chunk) {
42
49
  try {
43
50
  const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk, "utf8");
@@ -95,13 +102,15 @@ class SupervisorSocket {
95
102
  connectOnce = new Set();
96
103
  errorOnce = new Set();
97
104
  current;
98
- encoding = "utf8";
105
+ encoding;
99
106
  generation = 0;
100
107
  bind(socket) {
101
108
  this.current = socket;
102
109
  this.generation += 1;
103
110
  const generation = this.generation;
104
- socket.setEncoding(this.encoding);
111
+ if (this.encoding) {
112
+ socket.setEncoding(this.encoding);
113
+ }
105
114
  socket.on("data", (chunk) => {
106
115
  if (generation !== this.generation) {
107
116
  return;
@@ -163,6 +172,13 @@ class SupervisorSocket {
163
172
  }
164
173
  this.errorOnce.add(handler);
165
174
  }
175
+ off(event, handler) {
176
+ if (event === "connect") {
177
+ this.connectOnce.delete(handler);
178
+ return;
179
+ }
180
+ this.errorOnce.delete(handler);
181
+ }
166
182
  write(chunk) {
167
183
  if (!this.current) {
168
184
  throw new Error("Sidecar socket unavailable");
@@ -314,7 +330,7 @@ export function resolveConfiguredEndpoint(cfg) {
314
330
  export function daemonProvisioningHint() {
315
331
  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.";
316
332
  }
317
- export function defaultEndpoint(platform = process.platform, homeDir = os.homedir()) {
333
+ export function defaultEndpoint(platform = process.platform, homeDir = os.homedir(), pathExists = fs.existsSync) {
318
334
  // Honour the daemon's own env var first (set by Homebrew LaunchAgent / systemd unit).
319
335
  const envEndpoint = process.env.LIBRAVDB_RPC_ENDPOINT?.trim();
320
336
  if (envEndpoint && isConfiguredEndpoint(envEndpoint)) {
@@ -335,7 +351,7 @@ export function defaultEndpoint(platform = process.platform, homeDir = os.homedi
335
351
  for (const dir of candidateDirs) {
336
352
  const sockPath = path.join(dir, sockName);
337
353
  try {
338
- if (fs.existsSync(sockPath)) {
354
+ if (pathExists(sockPath)) {
339
355
  return `unix:${sockPath}`;
340
356
  }
341
357
  }
package/dist/types.d.ts CHANGED
@@ -131,6 +131,8 @@ export interface SidecarSocket {
131
131
  on(event: "error", handler: (error: Error) => void): void;
132
132
  once(event: "connect", handler: () => void): void;
133
133
  once(event: "error", handler: (error: Error) => void): void;
134
+ off(event: "connect", handler: () => void): void;
135
+ off(event: "error", handler: (error: Error) => void): void;
134
136
  write(chunk: Buffer | string): void;
135
137
  destroy(err?: Error): void;
136
138
  }
package/docs/install.md CHANGED
@@ -67,13 +67,22 @@ If you run the daemon on a non-default endpoint, add a plugin config:
67
67
  }
68
68
  ```
69
69
 
70
+ When `sidecarPath` is set to `"auto"`, the plugin resolves endpoints in this order on macOS/Linux:
71
+
72
+ 1. `LIBRAVDB_RPC_ENDPOINT` if it is set to a valid daemon endpoint
73
+ 2. `$HOME/.clawdb/run/libravdb.sock` if it exists
74
+ 3. `/opt/homebrew/var/clawdb/run/libravdb.sock` if it exists
75
+ 4. `/usr/local/var/clawdb/run/libravdb.sock` if it exists
76
+ 5. fallback to `$HOME/.clawdb/run/libravdb.sock`
77
+
70
78
  ## Sidecar Daemon Install
71
79
 
72
80
  The daemon owns the local database, embeddings, and JSON-RPC endpoint.
73
81
 
74
82
  Default endpoints:
75
83
 
76
- - macOS/Linux: `unix:$HOME/.clawdb/run/libravdb.sock`
84
+ - Homebrew on macOS: `unix:/opt/homebrew/var/clawdb/run/libravdb.sock`
85
+ - macOS/Linux user-local installs: `unix:$HOME/.clawdb/run/libravdb.sock`
77
86
  - Windows: `tcp:127.0.0.1:37421`
78
87
 
79
88
  Default data path:
@@ -142,7 +142,8 @@ Install and start `libravdbd` separately for the same user account that runs Ope
142
142
 
143
143
  Default endpoints:
144
144
 
145
- - macOS/Linux: `unix:$HOME/.clawdb/run/libravdb.sock`
145
+ - Homebrew on macOS: `unix:/opt/homebrew/var/clawdb/run/libravdb.sock`
146
+ - macOS/Linux user-local installs: `unix:$HOME/.clawdb/run/libravdb.sock`
146
147
  - Windows: `tcp:127.0.0.1:37421`
147
148
 
148
149
  If you run the daemon on a different endpoint, set `plugins.configs.libravdb-memory.sidecarPath` in `~/.openclaw/openclaw.json`.
@@ -176,6 +177,18 @@ brew install libravdbd
176
177
  brew services start libravdbd
177
178
  ```
178
179
 
180
+ With `sidecarPath: "auto"`, Homebrew installs on Apple Silicon should resolve to:
181
+
182
+ ```text
183
+ unix:/opt/homebrew/var/clawdb/run/libravdb.sock
184
+ ```
185
+
186
+ User-local installs still default to:
187
+
188
+ ```text
189
+ unix:$HOME/.clawdb/run/libravdb.sock
190
+ ```
191
+
179
192
  The daemon release pipeline generates a publish-ready `libravdbd.rb` formula asset for release assets named:
180
193
 
181
194
  - `libravdbd-darwin-arm64`
@@ -2,17 +2,35 @@
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.9",
6
- "kind": "context-engine",
5
+ "version": "1.4.11",
6
+ "kind": [
7
+ "memory",
8
+ "context-engine"
9
+ ],
7
10
  "configSchema": {
8
11
  "type": "object",
9
12
  "additionalProperties": false,
10
13
  "properties": {
11
- "dbPath": { "type": "string" },
12
- "sidecarPath": { "type": "string" },
13
- "useSessionSummarySearchExperiment": { "type": "boolean" },
14
- "embeddingRuntimePath": { "type": "string" },
15
- "embeddingBackend": { "type": "string", "enum": ["bundled", "onnx-local", "custom-local"] },
14
+ "dbPath": {
15
+ "type": "string"
16
+ },
17
+ "sidecarPath": {
18
+ "type": "string"
19
+ },
20
+ "useSessionSummarySearchExperiment": {
21
+ "type": "boolean"
22
+ },
23
+ "embeddingRuntimePath": {
24
+ "type": "string"
25
+ },
26
+ "embeddingBackend": {
27
+ "type": "string",
28
+ "enum": [
29
+ "bundled",
30
+ "onnx-local",
31
+ "custom-local"
32
+ ]
33
+ },
16
34
  "embeddingProfile": {
17
35
  "type": "string",
18
36
  "default": "all-minilm-l6-v2"
@@ -21,22 +39,60 @@
21
39
  "type": "string",
22
40
  "default": "all-minilm-l6-v2"
23
41
  },
24
- "embeddingModelPath": { "type": "string" },
25
- "embeddingTokenizerPath": { "type": "string" },
26
- "embeddingDimensions": { "type": "number" },
27
- "embeddingNormalize": { "type": "boolean" },
28
- "summarizerBackend": { "type": "string", "enum": ["bundled", "onnx-local", "ollama-local", "custom-local"] },
29
- "summarizerProfile": { "type": "string" },
30
- "summarizerRuntimePath": { "type": "string" },
31
- "summarizerModelPath": { "type": "string" },
32
- "summarizerTokenizerPath": { "type": "string" },
33
- "summarizerModel": { "type": "string" },
34
- "summarizerEndpoint": { "type": "string" },
35
- "sessionTTL": { "type": "number" },
36
- "topK": { "type": "number" },
37
- "alpha": { "type": "number" },
38
- "beta": { "type": "number" },
39
- "gamma": { "type": "number" },
42
+ "embeddingModelPath": {
43
+ "type": "string"
44
+ },
45
+ "embeddingTokenizerPath": {
46
+ "type": "string"
47
+ },
48
+ "embeddingDimensions": {
49
+ "type": "number"
50
+ },
51
+ "embeddingNormalize": {
52
+ "type": "boolean"
53
+ },
54
+ "summarizerBackend": {
55
+ "type": "string",
56
+ "enum": [
57
+ "bundled",
58
+ "onnx-local",
59
+ "ollama-local",
60
+ "custom-local"
61
+ ]
62
+ },
63
+ "summarizerProfile": {
64
+ "type": "string"
65
+ },
66
+ "summarizerRuntimePath": {
67
+ "type": "string"
68
+ },
69
+ "summarizerModelPath": {
70
+ "type": "string"
71
+ },
72
+ "summarizerTokenizerPath": {
73
+ "type": "string"
74
+ },
75
+ "summarizerModel": {
76
+ "type": "string"
77
+ },
78
+ "summarizerEndpoint": {
79
+ "type": "string"
80
+ },
81
+ "sessionTTL": {
82
+ "type": "number"
83
+ },
84
+ "topK": {
85
+ "type": "number"
86
+ },
87
+ "alpha": {
88
+ "type": "number"
89
+ },
90
+ "beta": {
91
+ "type": "number"
92
+ },
93
+ "gamma": {
94
+ "type": "number"
95
+ },
40
96
  "ingestionGateThreshold": {
41
97
  "type": "number",
42
98
  "default": 0.35
@@ -47,7 +103,9 @@
47
103
  },
48
104
  "markdownIngestionRoots": {
49
105
  "type": "array",
50
- "items": { "type": "string" }
106
+ "items": {
107
+ "type": "string"
108
+ }
51
109
  },
52
110
  "markdownIngestionObsidianEnabled": {
53
111
  "type": "boolean",
@@ -55,15 +113,21 @@
55
113
  },
56
114
  "markdownIngestionObsidianRoots": {
57
115
  "type": "array",
58
- "items": { "type": "string" }
116
+ "items": {
117
+ "type": "string"
118
+ }
59
119
  },
60
120
  "markdownIngestionObsidianInclude": {
61
121
  "type": "array",
62
- "items": { "type": "string" }
122
+ "items": {
123
+ "type": "string"
124
+ }
63
125
  },
64
126
  "markdownIngestionObsidianExclude": {
65
127
  "type": "array",
66
- "items": { "type": "string" }
128
+ "items": {
129
+ "type": "string"
130
+ }
67
131
  },
68
132
  "markdownIngestionObsidianDebounceMs": {
69
133
  "type": "number",
@@ -71,11 +135,15 @@
71
135
  },
72
136
  "markdownIngestionInclude": {
73
137
  "type": "array",
74
- "items": { "type": "string" }
138
+ "items": {
139
+ "type": "string"
140
+ }
75
141
  },
76
142
  "markdownIngestionExclude": {
77
143
  "type": "array",
78
- "items": { "type": "string" }
144
+ "items": {
145
+ "type": "string"
146
+ }
79
147
  },
80
148
  "markdownIngestionCollection": {
81
149
  "type": "string",
@@ -103,16 +171,38 @@
103
171
  "type": "object",
104
172
  "additionalProperties": false,
105
173
  "default": {
106
- "w1c": 0.35, "w2c": 0.40, "w3c": 0.25,
107
- "w1t": 0.40, "w2t": 0.35, "w3t": 0.25
174
+ "w1c": 0.35,
175
+ "w2c": 0.4,
176
+ "w3c": 0.25,
177
+ "w1t": 0.4,
178
+ "w2t": 0.35,
179
+ "w3t": 0.25
108
180
  },
109
181
  "properties": {
110
- "w1c": { "type": "number", "default": 0.35 },
111
- "w2c": { "type": "number", "default": 0.40 },
112
- "w3c": { "type": "number", "default": 0.25 },
113
- "w1t": { "type": "number", "default": 0.40 },
114
- "w2t": { "type": "number", "default": 0.35 },
115
- "w3t": { "type": "number", "default": 0.25 }
182
+ "w1c": {
183
+ "type": "number",
184
+ "default": 0.35
185
+ },
186
+ "w2c": {
187
+ "type": "number",
188
+ "default": 0.4
189
+ },
190
+ "w3c": {
191
+ "type": "number",
192
+ "default": 0.25
193
+ },
194
+ "w1t": {
195
+ "type": "number",
196
+ "default": 0.4
197
+ },
198
+ "w2t": {
199
+ "type": "number",
200
+ "default": 0.35
201
+ },
202
+ "w3t": {
203
+ "type": "number",
204
+ "default": 0.25
205
+ }
116
206
  }
117
207
  },
118
208
  "gatingTechNorm": {
@@ -132,21 +222,42 @@
132
222
  "default": 0.5,
133
223
  "description": "Controls how much summary confidence affects retrieval score. 0 ignores summary quality and 1 fully suppresses zero-confidence summaries."
134
224
  },
135
- "recencyLambdaSession": { "type": "number" },
136
- "recencyLambdaUser": { "type": "number" },
137
- "recencyLambdaGlobal": { "type": "number" },
138
- "tokenBudgetFraction": { "type": "number" },
139
- "compactThreshold": { "type": "number" },
225
+ "recencyLambdaSession": {
226
+ "type": "number"
227
+ },
228
+ "recencyLambdaUser": {
229
+ "type": "number"
230
+ },
231
+ "recencyLambdaGlobal": {
232
+ "type": "number"
233
+ },
234
+ "tokenBudgetFraction": {
235
+ "type": "number"
236
+ },
237
+ "compactThreshold": {
238
+ "type": "number"
239
+ },
140
240
  "compactSessionTokenBudget": {
141
241
  "type": "number",
142
242
  "default": 2000,
143
243
  "description": "Auto-trigger compaction when the session accumulates this many tokens since the last compaction. Set to 0 to disable auto-compaction."
144
244
  },
145
- "ollamaUrl": { "type": "string" },
146
- "compactModel": { "type": "string" },
147
- "rpcTimeoutMs": { "type": "number", "default": 30000 },
148
- "maxRetries": { "type": "number" },
149
- "logLevel": { "type": "string" }
245
+ "ollamaUrl": {
246
+ "type": "string"
247
+ },
248
+ "compactModel": {
249
+ "type": "string"
250
+ },
251
+ "rpcTimeoutMs": {
252
+ "type": "number",
253
+ "default": 30000
254
+ },
255
+ "maxRetries": {
256
+ "type": "number"
257
+ },
258
+ "logLevel": {
259
+ "type": "string"
260
+ }
150
261
  }
151
262
  }
152
263
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xdarkicex/openclaw-memory-libravdb",
3
- "version": "1.4.9",
3
+ "version": "1.4.11",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",