@xdarkicex/openclaw-memory-libravdb 1.4.10 → 1.4.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.
@@ -17,6 +17,18 @@ type OpenClawCompatibleAssembleResult = {
17
17
  systemPromptAddition: string;
18
18
  debug?: AssembleContextInternalResponse["debug"];
19
19
  };
20
+ type OpenClawCompatibleCompactResult = {
21
+ ok: boolean;
22
+ compacted: boolean;
23
+ reason?: string;
24
+ result?: {
25
+ summary?: string;
26
+ firstKeptEntryId?: string;
27
+ tokensBefore: number;
28
+ tokensAfter?: number;
29
+ details?: unknown;
30
+ };
31
+ };
20
32
  export declare function normalizeKernelMessage(message: {
21
33
  role: string;
22
34
  content: unknown;
@@ -76,7 +88,8 @@ export declare function buildContextEngineFactory(runtime: PluginRuntime, cfg: P
76
88
  sessionId: string;
77
89
  force?: boolean;
78
90
  targetSize?: number;
79
- }): Promise<any>;
91
+ tokenBudget?: number;
92
+ }): Promise<OpenClawCompatibleCompactResult>;
80
93
  afterTurn(args: {
81
94
  sessionId: string;
82
95
  sessionKey?: string;
@@ -88,6 +101,8 @@ export declare function buildContextEngineFactory(runtime: PluginRuntime, cfg: P
88
101
  }>;
89
102
  prePromptMessageCount?: number;
90
103
  isHeartbeat?: boolean;
104
+ tokenBudget?: number;
105
+ runtimeContext?: Record<string, unknown>;
91
106
  }): Promise<any>;
92
107
  };
93
108
  export {};
@@ -1,3 +1,34 @@
1
+ const APPROX_CHARS_PER_TOKEN = 4;
2
+ const ASSEMBLE_BUDGET_HEADROOM_TOKENS = 256;
3
+ function requireSessionId(sessionId, operation) {
4
+ const normalized = typeof sessionId === "string" ? sessionId.trim() : "";
5
+ if (normalized.length > 0) {
6
+ return normalized;
7
+ }
8
+ throw new Error(`LibraVDB ${operation} requires a non-empty sessionId; refusing ambiguous request.`);
9
+ }
10
+ function normalizeCompactResult(response) {
11
+ const didCompact = response?.didCompact === true;
12
+ const details = {
13
+ clustersFormed: typeof response?.clustersFormed === "number" ? response.clustersFormed : undefined,
14
+ clustersDeclined: typeof response?.clustersDeclined === "number" ? response.clustersDeclined : undefined,
15
+ turnsRemoved: typeof response?.turnsRemoved === "number" ? response.turnsRemoved : undefined,
16
+ summaryMethod: typeof response?.summaryMethod === "string" && response.summaryMethod.length > 0
17
+ ? response.summaryMethod
18
+ : undefined,
19
+ meanConfidence: typeof response?.meanConfidence === "number" ? response.meanConfidence : undefined,
20
+ };
21
+ return {
22
+ ok: true,
23
+ compacted: didCompact,
24
+ ...(didCompact ? {} : { reason: "not_compacted" }),
25
+ result: {
26
+ tokensBefore: 0,
27
+ ...(details.summaryMethod ? { summary: details.summaryMethod } : {}),
28
+ details,
29
+ },
30
+ };
31
+ }
1
32
  function describeUnexpectedContent(value) {
2
33
  try {
3
34
  const serialized = JSON.stringify(value);
@@ -57,6 +88,72 @@ function normalizeKernelContent(content) {
57
88
  }
58
89
  return content.map(stringifyKernelBlock).filter((part) => part.length > 0).join("\n");
59
90
  }
91
+ function approximateTokenCount(text) {
92
+ if (!text)
93
+ return 0;
94
+ return Math.ceil(text.length / APPROX_CHARS_PER_TOKEN);
95
+ }
96
+ function approximateMessageTokens(message) {
97
+ // Approximate per-message wrapper overhead so trimming is conservative.
98
+ return approximateTokenCount(message.content) + 8;
99
+ }
100
+ function approximateMessagesTokens(messages) {
101
+ return messages.reduce((sum, message) => sum + approximateMessageTokens(message), 0);
102
+ }
103
+ function truncateContentToTokenBudget(content, tokenBudget) {
104
+ if (tokenBudget <= 0)
105
+ return "";
106
+ const maxChars = Math.max(1, tokenBudget * APPROX_CHARS_PER_TOKEN);
107
+ if (content.length <= maxChars)
108
+ return content;
109
+ // Keep the tail so recent tool output / latest answer content is preserved.
110
+ return content.slice(content.length - maxChars);
111
+ }
112
+ function trimMessagesToBudget(messages, tokenBudget) {
113
+ if (tokenBudget <= 0 || messages.length === 0) {
114
+ return [];
115
+ }
116
+ const kept = [];
117
+ let used = 0;
118
+ for (let i = messages.length - 1; i >= 0; i -= 1) {
119
+ const candidate = messages[i];
120
+ const cost = approximateMessageTokens(candidate);
121
+ if (used + cost > tokenBudget) {
122
+ continue;
123
+ }
124
+ kept.push(candidate);
125
+ used += cost;
126
+ }
127
+ if (kept.length > 0) {
128
+ return kept.reverse();
129
+ }
130
+ const last = messages[messages.length - 1];
131
+ const contentBudget = Math.max(1, tokenBudget - 8);
132
+ const truncated = truncateContentToTokenBudget(last.content, contentBudget);
133
+ if (!truncated) {
134
+ return [];
135
+ }
136
+ return [{ ...last, content: truncated }];
137
+ }
138
+ function enforceTokenBudgetInvariant(result, tokenBudget) {
139
+ if (typeof tokenBudget !== "number" || !Number.isFinite(tokenBudget) || tokenBudget <= 0) {
140
+ return result;
141
+ }
142
+ const hardBudget = Math.max(1, Math.floor(tokenBudget));
143
+ const effectiveBudget = Math.max(1, hardBudget - ASSEMBLE_BUDGET_HEADROOM_TOKENS);
144
+ const estimated = typeof result.estimatedTokens === "number" ? result.estimatedTokens : 0;
145
+ const approxFromMessages = approximateMessagesTokens(result.messages);
146
+ if (estimated <= effectiveBudget && approxFromMessages <= effectiveBudget) {
147
+ return result;
148
+ }
149
+ const trimmedMessages = trimMessagesToBudget(result.messages, effectiveBudget);
150
+ const trimmedEstimate = approximateMessagesTokens(trimmedMessages);
151
+ return {
152
+ ...result,
153
+ messages: trimmedMessages,
154
+ estimatedTokens: Math.min(effectiveBudget, trimmedEstimate),
155
+ };
156
+ }
60
157
  export function normalizeKernelMessage(message) {
61
158
  return {
62
159
  role: message.role,
@@ -87,6 +184,27 @@ export function normalizeAssembleResult(result) {
87
184
  };
88
185
  }
89
186
  export function buildContextEngineFactory(runtime, cfg, recallCache, logger = console) {
187
+ function buildCompactSessionRequest(args) {
188
+ // OpenClaw core now requests budget-style compaction using tokenBudget,
189
+ // but the current LibraVDB compact_session wire contract still expects
190
+ // targetSize. Use tokenBudget as the compatibility target so overflow and
191
+ // timeout retries still compact toward the host's requested prompt budget.
192
+ const targetSize = args.targetSize ?? args.tokenBudget;
193
+ return {
194
+ sessionId: requireSessionId(args.sessionId, "compact"),
195
+ force: args.force,
196
+ ...(typeof targetSize === "number" ? { targetSize } : {}),
197
+ ...(typeof cfg.continuityMinTurns === "number"
198
+ ? { continuityMinTurns: cfg.continuityMinTurns }
199
+ : {}),
200
+ ...(typeof cfg.continuityTailBudgetTokens === "number"
201
+ ? { continuityTailBudgetTokens: cfg.continuityTailBudgetTokens }
202
+ : {}),
203
+ ...(typeof cfg.continuityPriorContextTokens === "number"
204
+ ? { continuityPriorContextTokens: cfg.continuityPriorContextTokens }
205
+ : {}),
206
+ };
207
+ }
90
208
  return {
91
209
  info: { id: "libravdb-memory", name: "LibraVDB Memory", ownsCompaction: true },
92
210
  ownsCompaction: true,
@@ -133,71 +251,90 @@ export function buildContextEngineFactory(runtime, cfg, recallCache, logger = co
133
251
  const messages = normalizeKernelMessages(args.messages);
134
252
  const kernel = runtime.getKernel();
135
253
  if (kernel) {
136
- return normalizeAssembleResult(await kernel.assembleContext({
254
+ try {
255
+ return enforceTokenBudgetInvariant(normalizeAssembleResult(await kernel.assembleContext({
256
+ sessionId: args.sessionId,
257
+ sessionKey: args.sessionKey,
258
+ userId: args.userId,
259
+ queryText: args.prompt ?? "",
260
+ visibleMessages: messages,
261
+ tokenBudget: args.tokenBudget,
262
+ config: {},
263
+ emitDebug: true
264
+ })), args.tokenBudget);
265
+ }
266
+ catch (error) {
267
+ logger.warn?.(`LibraVDB assemble kernel failed, using budget-clamped fallback context: ${error instanceof Error ? error.message : String(error)}`);
268
+ const fallbackMessages = trimMessagesToBudget(messages.map((message) => ({ ...message })), Math.max(1, Math.floor(args.tokenBudget) - ASSEMBLE_BUDGET_HEADROOM_TOKENS));
269
+ return {
270
+ messages: fallbackMessages,
271
+ estimatedTokens: approximateMessagesTokens(fallbackMessages),
272
+ systemPromptAddition: "",
273
+ };
274
+ }
275
+ }
276
+ const rpc = await runtime.getRpc();
277
+ try {
278
+ const resp = await rpc.call("assemble_context_internal", {
137
279
  sessionId: args.sessionId,
138
280
  sessionKey: args.sessionKey,
139
281
  userId: args.userId,
140
- queryText: args.prompt ?? "",
141
- visibleMessages: messages,
282
+ messages,
142
283
  tokenBudget: args.tokenBudget,
143
- config: {},
144
- emitDebug: true
145
- }));
284
+ prompt: args.prompt,
285
+ emitDebug: true,
286
+ config: {
287
+ useSessionRecallProjection: cfg.useSessionRecallProjection,
288
+ useSessionSummarySearchExperiment: cfg.useSessionSummarySearchExperiment,
289
+ tokenBudgetFraction: cfg.tokenBudgetFraction,
290
+ authoredHardBudgetFraction: cfg.authoredHardBudgetFraction,
291
+ authoredSoftBudgetFraction: cfg.authoredSoftBudgetFraction,
292
+ elevatedGuidanceBudgetFraction: cfg.elevatedGuidanceBudgetFraction,
293
+ topK: cfg.topK,
294
+ continuityMinTurns: cfg.continuityMinTurns,
295
+ continuityTailBudgetTokens: cfg.continuityTailBudgetTokens,
296
+ continuityPriorContextTokens: cfg.continuityPriorContextTokens,
297
+ compactThreshold: cfg.compactThreshold,
298
+ compactSessionTokenBudget: cfg.compactSessionTokenBudget,
299
+ section7Theta1: cfg.section7Theta1,
300
+ section7Kappa: cfg.section7Kappa,
301
+ section7HopEta: cfg.section7HopEta,
302
+ section7HopThreshold: cfg.section7HopThreshold,
303
+ section7CoarseTopK: cfg.section7CoarseTopK,
304
+ section7SecondPassTopK: cfg.section7SecondPassTopK,
305
+ section7AuthorityRecencyLambda: cfg.section7AuthorityRecencyLambda,
306
+ section7AuthorityRecencyWeight: cfg.section7AuthorityRecencyWeight,
307
+ section7AuthorityFrequencyWeight: cfg.section7AuthorityFrequencyWeight,
308
+ section7AuthorityAuthoredWeight: cfg.section7AuthorityAuthoredWeight,
309
+ recoveryFloorScore: cfg.recoveryFloorScore,
310
+ recoveryMinTopK: cfg.recoveryMinTopK,
311
+ recoveryMinConfidenceMean: cfg.recoveryMinConfidenceMean,
312
+ recencyLambdaSession: cfg.recencyLambdaSession,
313
+ recencyLambdaUser: cfg.recencyLambdaUser,
314
+ recencyLambdaGlobal: cfg.recencyLambdaGlobal,
315
+ ingestionGateThreshold: cfg.ingestionGateThreshold,
316
+ },
317
+ });
318
+ return enforceTokenBudgetInvariant(normalizeAssembleResult(resp), args.tokenBudget);
319
+ }
320
+ catch (error) {
321
+ logger.warn?.(`LibraVDB assemble sidecar failed, using budget-clamped fallback context: ${error instanceof Error ? error.message : String(error)}`);
322
+ const fallbackMessages = trimMessagesToBudget(messages.map((message) => ({ ...message })), Math.max(1, Math.floor(args.tokenBudget) - ASSEMBLE_BUDGET_HEADROOM_TOKENS));
323
+ return {
324
+ messages: fallbackMessages,
325
+ estimatedTokens: approximateMessagesTokens(fallbackMessages),
326
+ systemPromptAddition: "",
327
+ };
146
328
  }
147
- const rpc = await runtime.getRpc();
148
- const resp = await rpc.call("assemble_context_internal", {
149
- sessionId: args.sessionId,
150
- sessionKey: args.sessionKey,
151
- userId: args.userId,
152
- messages,
153
- tokenBudget: args.tokenBudget,
154
- prompt: args.prompt,
155
- emitDebug: true,
156
- config: {
157
- useSessionRecallProjection: cfg.useSessionRecallProjection,
158
- useSessionSummarySearchExperiment: cfg.useSessionSummarySearchExperiment,
159
- tokenBudgetFraction: cfg.tokenBudgetFraction,
160
- authoredHardBudgetFraction: cfg.authoredHardBudgetFraction,
161
- authoredSoftBudgetFraction: cfg.authoredSoftBudgetFraction,
162
- elevatedGuidanceBudgetFraction: cfg.elevatedGuidanceBudgetFraction,
163
- topK: cfg.topK,
164
- continuityMinTurns: cfg.continuityMinTurns,
165
- continuityTailBudgetTokens: cfg.continuityTailBudgetTokens,
166
- continuityPriorContextTokens: cfg.continuityPriorContextTokens,
167
- compactThreshold: cfg.compactThreshold,
168
- compactSessionTokenBudget: cfg.compactSessionTokenBudget,
169
- section7Theta1: cfg.section7Theta1,
170
- section7Kappa: cfg.section7Kappa,
171
- section7HopEta: cfg.section7HopEta,
172
- section7HopThreshold: cfg.section7HopThreshold,
173
- section7CoarseTopK: cfg.section7CoarseTopK,
174
- section7SecondPassTopK: cfg.section7SecondPassTopK,
175
- section7AuthorityRecencyLambda: cfg.section7AuthorityRecencyLambda,
176
- section7AuthorityRecencyWeight: cfg.section7AuthorityRecencyWeight,
177
- section7AuthorityFrequencyWeight: cfg.section7AuthorityFrequencyWeight,
178
- section7AuthorityAuthoredWeight: cfg.section7AuthorityAuthoredWeight,
179
- recoveryFloorScore: cfg.recoveryFloorScore,
180
- recoveryMinTopK: cfg.recoveryMinTopK,
181
- recoveryMinConfidenceMean: cfg.recoveryMinConfidenceMean,
182
- recencyLambdaSession: cfg.recencyLambdaSession,
183
- recencyLambdaUser: cfg.recencyLambdaUser,
184
- recencyLambdaGlobal: cfg.recencyLambdaGlobal,
185
- ingestionGateThreshold: cfg.ingestionGateThreshold,
186
- },
187
- });
188
- return normalizeAssembleResult(resp);
189
329
  },
190
330
  async compact(args) {
331
+ const request = buildCompactSessionRequest(args);
191
332
  const kernel = runtime.getKernel();
192
333
  if (kernel) {
193
- return await kernel.compactSession({
194
- sessionId: args.sessionId,
195
- force: args.force,
196
- targetSize: args.targetSize,
197
- });
334
+ return normalizeCompactResult(await kernel.compactSession(request));
198
335
  }
199
336
  const rpc = await runtime.getRpc();
200
- return await rpc.call("compact_session", args);
337
+ return normalizeCompactResult(await rpc.call("compact_session", request));
201
338
  },
202
339
  async afterTurn(args) {
203
340
  const messages = normalizeKernelMessages(args.messages);
@@ -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.10",
5
+ "version": "1.4.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.4.10",
3
+ "version": "1.4.12",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",