@xdarkicex/openclaw-memory-libravdb 1.4.11 → 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;
@@ -77,7 +89,7 @@ export declare function buildContextEngineFactory(runtime: PluginRuntime, cfg: P
77
89
  force?: boolean;
78
90
  targetSize?: number;
79
91
  tokenBudget?: number;
80
- }): Promise<any>;
92
+ }): Promise<OpenClawCompatibleCompactResult>;
81
93
  afterTurn(args: {
82
94
  sessionId: string;
83
95
  sessionKey?: string;
@@ -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,
@@ -94,7 +191,7 @@ export function buildContextEngineFactory(runtime, cfg, recallCache, logger = co
94
191
  // timeout retries still compact toward the host's requested prompt budget.
95
192
  const targetSize = args.targetSize ?? args.tokenBudget;
96
193
  return {
97
- sessionId: args.sessionId,
194
+ sessionId: requireSessionId(args.sessionId, "compact"),
98
195
  force: args.force,
99
196
  ...(typeof targetSize === "number" ? { targetSize } : {}),
100
197
  ...(typeof cfg.continuityMinTurns === "number"
@@ -154,68 +251,90 @@ export function buildContextEngineFactory(runtime, cfg, recallCache, logger = co
154
251
  const messages = normalizeKernelMessages(args.messages);
155
252
  const kernel = runtime.getKernel();
156
253
  if (kernel) {
157
- 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", {
158
279
  sessionId: args.sessionId,
159
280
  sessionKey: args.sessionKey,
160
281
  userId: args.userId,
161
- queryText: args.prompt ?? "",
162
- visibleMessages: messages,
282
+ messages,
163
283
  tokenBudget: args.tokenBudget,
164
- config: {},
165
- emitDebug: true
166
- }));
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
+ };
167
328
  }
168
- const rpc = await runtime.getRpc();
169
- const resp = await rpc.call("assemble_context_internal", {
170
- sessionId: args.sessionId,
171
- sessionKey: args.sessionKey,
172
- userId: args.userId,
173
- messages,
174
- tokenBudget: args.tokenBudget,
175
- prompt: args.prompt,
176
- emitDebug: true,
177
- config: {
178
- useSessionRecallProjection: cfg.useSessionRecallProjection,
179
- useSessionSummarySearchExperiment: cfg.useSessionSummarySearchExperiment,
180
- tokenBudgetFraction: cfg.tokenBudgetFraction,
181
- authoredHardBudgetFraction: cfg.authoredHardBudgetFraction,
182
- authoredSoftBudgetFraction: cfg.authoredSoftBudgetFraction,
183
- elevatedGuidanceBudgetFraction: cfg.elevatedGuidanceBudgetFraction,
184
- topK: cfg.topK,
185
- continuityMinTurns: cfg.continuityMinTurns,
186
- continuityTailBudgetTokens: cfg.continuityTailBudgetTokens,
187
- continuityPriorContextTokens: cfg.continuityPriorContextTokens,
188
- compactThreshold: cfg.compactThreshold,
189
- compactSessionTokenBudget: cfg.compactSessionTokenBudget,
190
- section7Theta1: cfg.section7Theta1,
191
- section7Kappa: cfg.section7Kappa,
192
- section7HopEta: cfg.section7HopEta,
193
- section7HopThreshold: cfg.section7HopThreshold,
194
- section7CoarseTopK: cfg.section7CoarseTopK,
195
- section7SecondPassTopK: cfg.section7SecondPassTopK,
196
- section7AuthorityRecencyLambda: cfg.section7AuthorityRecencyLambda,
197
- section7AuthorityRecencyWeight: cfg.section7AuthorityRecencyWeight,
198
- section7AuthorityFrequencyWeight: cfg.section7AuthorityFrequencyWeight,
199
- section7AuthorityAuthoredWeight: cfg.section7AuthorityAuthoredWeight,
200
- recoveryFloorScore: cfg.recoveryFloorScore,
201
- recoveryMinTopK: cfg.recoveryMinTopK,
202
- recoveryMinConfidenceMean: cfg.recoveryMinConfidenceMean,
203
- recencyLambdaSession: cfg.recencyLambdaSession,
204
- recencyLambdaUser: cfg.recencyLambdaUser,
205
- recencyLambdaGlobal: cfg.recencyLambdaGlobal,
206
- ingestionGateThreshold: cfg.ingestionGateThreshold,
207
- },
208
- });
209
- return normalizeAssembleResult(resp);
210
329
  },
211
330
  async compact(args) {
212
331
  const request = buildCompactSessionRequest(args);
213
332
  const kernel = runtime.getKernel();
214
333
  if (kernel) {
215
- return await kernel.compactSession(request);
334
+ return normalizeCompactResult(await kernel.compactSession(request));
216
335
  }
217
336
  const rpc = await runtime.getRpc();
218
- return await rpc.call("compact_session", request);
337
+ return normalizeCompactResult(await rpc.call("compact_session", request));
219
338
  },
220
339
  async afterTurn(args) {
221
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.11",
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.11",
3
+ "version": "1.4.12",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",