@openviking/openclaw-plugin 2026.5.26 → 2026.6.2

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,7 @@
1
1
  import { pickMemoriesForInjection, postProcessMemories, summarizeInjectionMemories, toJsonLog, } from "./memory-ranking.js";
2
2
  import { quickRecallPrecheck, withTimeout } from "./process-manager.js";
3
3
  import { sanitizeUserTextForCapture } from "./text-utils.js";
4
+ import { estimateTextTokens } from "./token-estimator.js";
4
5
  const AUTO_RECALL_TIMEOUT_MS = 5_000;
5
6
  const RECALL_QUERY_MAX_CHARS = 4_000;
6
7
  export const AUTO_RECALL_SOURCE_MARKER = "Source: openviking-auto-recall";
@@ -25,11 +26,29 @@ export function prepareRecallQuery(rawText) {
25
26
  finalChars: query.length,
26
27
  };
27
28
  }
28
- /** Estimate token count using chars/4 heuristic for diagnostics. */
29
+ /** Estimate token count using the shared CJK-aware fallback for diagnostics. */
29
30
  export function estimateTokenCount(text) {
30
- if (!text)
31
- return 0;
32
- return Math.ceil(text.length / 4);
31
+ return estimateTextTokens(text);
32
+ }
33
+ function memoryCategory(item) {
34
+ return item.category?.trim() || "memory";
35
+ }
36
+ function indentContent(content) {
37
+ return content
38
+ .split("\n")
39
+ .map((line) => ` ${line}`)
40
+ .join("\n");
41
+ }
42
+ function formatMemoryLine(item, content, options) {
43
+ const category = memoryCategory(item);
44
+ if (!options.includeUri) {
45
+ return `- [${category}] ${content}`;
46
+ }
47
+ return [
48
+ `- [${category}]`,
49
+ ` <uri>${item.uri}</uri>`,
50
+ indentContent(content),
51
+ ].join("\n");
33
52
  }
34
53
  async function resolveMemoryContent(item, readFn, options) {
35
54
  let content;
@@ -57,7 +76,7 @@ export async function buildMemoryLines(memories, readFn, options) {
57
76
  const lines = [];
58
77
  for (const item of memories) {
59
78
  const content = await resolveMemoryContent(item, readFn, options);
60
- lines.push(`- [${item.category ?? "memory"}] ${content}`);
79
+ lines.push(formatMemoryLine(item, content, options));
61
80
  }
62
81
  return lines;
63
82
  }
@@ -78,7 +97,7 @@ export async function buildMemoryLinesWithBudget(memories, readFn, options) {
78
97
  break;
79
98
  }
80
99
  const content = await resolveMemoryContent(item, readFn, options);
81
- const line = `- [${item.category ?? "memory"}] ${content}`;
100
+ const line = formatMemoryLine(item, content, options);
82
101
  const separatorChars = lines.length > 0 ? 1 : 0;
83
102
  const projectedChars = totalChars + separatorChars + line.length;
84
103
  if (projectedChars > charBudget) {
@@ -105,7 +124,7 @@ export async function buildAutoRecallContext(params) {
105
124
  if (!cfg.autoRecall || queryText.length < 5) {
106
125
  return { memoryCount: 0, estimatedTokens: 0 };
107
126
  }
108
- const precheck = await quickRecallPrecheck(cfg.baseUrl);
127
+ const precheck = await quickRecallPrecheck(client, agentId);
109
128
  if (!precheck.ok) {
110
129
  verbose?.(`openviking: skipping auto-recall because precheck failed (${precheck.reason})`);
111
130
  return { memoryCount: 0, estimatedTokens: 0 };
@@ -154,6 +173,7 @@ export async function buildAutoRecallContext(params) {
154
173
  const { lines: memoryLines, estimatedTokens } = await buildMemoryLinesWithBudget(memories, (uri) => client.read(uri, agentId), {
155
174
  recallPreferAbstract: cfg.recallPreferAbstract,
156
175
  recallMaxInjectedChars: cfg.recallMaxInjectedChars,
176
+ includeUri: true,
157
177
  });
158
178
  if (memoryLines.length === 0) {
159
179
  verbose?.(`openviking: skipping auto-recall injection; no complete memories fit maxInjectedChars=${cfg.recallMaxInjectedChars}`);
package/dist/client.js CHANGED
@@ -155,8 +155,8 @@ export class OpenVikingClient {
155
155
  clearTimeout(timer);
156
156
  }
157
157
  }
158
- async healthCheck() {
159
- await this.request("/health");
158
+ async healthCheck(agentId, requestTimeoutMs) {
159
+ await this.request("/health", {}, agentId, requestTimeoutMs);
160
160
  }
161
161
  async getRuntimeIdentity(agentId) {
162
162
  const effectiveAgentId = this.resolveEffectiveAgentId(agentId);
@@ -4,6 +4,7 @@ import { AUTO_RECALL_SOURCE_MARKER, buildAutoRecallContext, prepareRecallQuery,
4
4
  import { compileSessionPatterns, extractNewTurnMessages, shouldBypassSession, } from "./text-utils.js";
5
5
  import { toJsonLog, } from "./memory-ranking.js";
6
6
  import { sanitizeToolUseResultPairing } from "./session-transcript-repair.js";
7
+ import { estimateAgentMessageTokens, estimateAgentMessagesTokens, estimateTextTokens, } from "./token-estimator.js";
7
8
  export function toRoleId(senderId) {
8
9
  if (!senderId) {
9
10
  return undefined;
@@ -30,15 +31,10 @@ function allocateContextBudget(totalBudget, instructionTokens = 0) {
30
31
  return { archiveMemory, sessionContext, reserved };
31
32
  }
32
33
  function roughEstimate(messages) {
33
- return Math.ceil(JSON.stringify(messages).length / 4);
34
+ return estimateAgentMessagesTokens(messages);
34
35
  }
35
36
  function msgTokenEstimate(msg) {
36
- const raw = msg.content;
37
- if (typeof raw === "string")
38
- return Math.ceil(raw.length / 4);
39
- if (Array.isArray(raw))
40
- return Math.ceil(JSON.stringify(raw).length / 4);
41
- return 1;
37
+ return estimateAgentMessageTokens(msg);
42
38
  }
43
39
  function normalizeTimestamp(value) {
44
40
  if (typeof value === "number" && Number.isFinite(value)) {
@@ -503,7 +499,7 @@ function buildSystemPromptAddition() {
503
499
  }
504
500
  function buildInstructionPrompt() {
505
501
  const text = buildSystemPromptAddition();
506
- return { text, tokens: Math.ceil(text.length / 4) };
502
+ return { text, tokens: estimateTextTokens(text) };
507
503
  }
508
504
  function buildArchiveMemory(archiveOverview, _preAbstracts, _budget) {
509
505
  const messages = [];
package/dist/index.js CHANGED
@@ -8,6 +8,7 @@ import { clampScore, postProcessMemories, pickMemoriesForInjection, } from "./me
8
8
  import { createMemoryOpenVikingContextEngine, openClawSessionToOvStorageId, openClawSessionRefToOvStorageId, } from "./context-engine.js";
9
9
  import { buildMemoryLines, buildMemoryLinesWithBudget, estimateTokenCount, prepareRecallQuery, } from "./auto-recall.js";
10
10
  export { buildMemoryLines, buildMemoryLinesWithBudget, estimateTokenCount, prepareRecallQuery, };
11
+ export { estimateAgentMessageTokens, estimateAgentMessagesTokens, estimateSerializedTokens, estimateTextTokens, } from "./token-estimator.js";
11
12
  function parseToolResultRef(value) {
12
13
  const raw = typeof value === "string" ? value.trim() : "";
13
14
  if (!raw) {
@@ -848,7 +849,7 @@ const contextEnginePlugin = {
848
849
  api.registerTool((ctx) => ({
849
850
  name: "memory_store",
850
851
  label: "Memory Store (OpenViking)",
851
- description: "Store text in OpenViking memory pipeline by writing to a session and running memory extraction.",
852
+ description: "Store text in OpenViking memory pipeline by writing to a session and running memory extraction. Use when the user explicitly asks to remember, save, or store an important long-term fact, preference, project, or decision; automatic capture is threshold/commit dependent.",
852
853
  parameters: Type.Object({
853
854
  text: Type.String({ description: "Information to store as memory source text" }),
854
855
  role: Type.Optional(Type.String({ description: "Session role, default user" })),
@@ -914,7 +915,25 @@ const contextEnginePlugin = {
914
915
  }
915
916
  if (memoriesCount === 0) {
916
917
  api.logger.warn(`openviking: memory_store committed but 0 memories extracted (sessionId=${sessionId}). ` +
917
- "Check OpenViking server logs for embedding/extract errors (e.g. 401 API key, or extraction pipeline).");
918
+ "No OpenViking-managed long-term memory was created. Check memory.extraction_enabled, VLM configuration/API keys, or whether the text was judged not durable.");
919
+ return {
920
+ content: [
921
+ {
922
+ type: "text",
923
+ text: `Memory extraction completed for session ${sessionId}, but produced 0 memories. ` +
924
+ "No OpenViking-managed long-term memory was stored. Check memory.extraction_enabled, VLM configuration/API keys, or whether the text is durable enough to remember.",
925
+ },
926
+ ],
927
+ details: {
928
+ action: "failed",
929
+ sessionId,
930
+ status: commitResult.status,
931
+ error: "no_memories_extracted",
932
+ memoriesCount,
933
+ archived: commitResult.archived ?? false,
934
+ usedTempSession,
935
+ },
936
+ };
918
937
  }
919
938
  else {
920
939
  api.logger.info?.(`openviking: memory_store committed, memories=${memoriesCount}`);
@@ -10,29 +10,17 @@ export function withTimeout(promise, timeoutMs, timeoutMessage) {
10
10
  });
11
11
  });
12
12
  }
13
- export async function quickHealthCheck(baseUrl, timeoutMs) {
14
- const controller = new AbortController();
15
- const timer = setTimeout(() => controller.abort(), timeoutMs);
13
+ export async function quickHealthCheck(client, agentId, timeoutMs) {
16
14
  try {
17
- const response = await fetch(`${baseUrl}/health`, {
18
- method: "GET",
19
- signal: controller.signal,
20
- });
21
- if (!response.ok) {
22
- return false;
23
- }
24
- const body = (await response.json().catch(() => ({})));
25
- return body.status === "ok";
15
+ await client.healthCheck(agentId, timeoutMs);
16
+ return true;
26
17
  }
27
18
  catch {
28
19
  return false;
29
20
  }
30
- finally {
31
- clearTimeout(timer);
32
- }
33
21
  }
34
- export async function quickRecallPrecheck(baseUrl) {
35
- const healthOk = await quickHealthCheck(baseUrl, 500);
22
+ export async function quickRecallPrecheck(client, agentId) {
23
+ const healthOk = await quickHealthCheck(client, agentId, 500);
36
24
  if (healthOk) {
37
25
  return { ok: true };
38
26
  }
@@ -0,0 +1,51 @@
1
+ function isCjkCodePoint(codePoint) {
2
+ return ((codePoint >= 0x3400 && codePoint <= 0x4dbf) ||
3
+ (codePoint >= 0x4e00 && codePoint <= 0x9fff) ||
4
+ (codePoint >= 0xf900 && codePoint <= 0xfaff) ||
5
+ (codePoint >= 0x20000 && codePoint <= 0x2ebef) ||
6
+ (codePoint >= 0x3040 && codePoint <= 0x30ff) ||
7
+ (codePoint >= 0x31f0 && codePoint <= 0x31ff) ||
8
+ (codePoint >= 0xac00 && codePoint <= 0xd7af) ||
9
+ (codePoint >= 0x1100 && codePoint <= 0x11ff) ||
10
+ (codePoint >= 0x3130 && codePoint <= 0x318f) ||
11
+ (codePoint >= 0xff00 && codePoint <= 0xffef) ||
12
+ (codePoint >= 0x3000 && codePoint <= 0x303f));
13
+ }
14
+ function codePointWeight(codePoint) {
15
+ if (isCjkCodePoint(codePoint)) {
16
+ return 1.5;
17
+ }
18
+ if (codePoint > 0xffff) {
19
+ return 2;
20
+ }
21
+ return 0.25;
22
+ }
23
+ export function estimateTextTokens(text) {
24
+ if (!text) {
25
+ return 0;
26
+ }
27
+ let weightedTokens = 0;
28
+ for (const char of text) {
29
+ weightedTokens += codePointWeight(char.codePointAt(0) ?? 0);
30
+ }
31
+ return Math.ceil(weightedTokens);
32
+ }
33
+ export function estimateSerializedTokens(value) {
34
+ if (value === null || value === undefined) {
35
+ return 0;
36
+ }
37
+ if (typeof value === "string") {
38
+ return estimateTextTokens(value);
39
+ }
40
+ if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
41
+ return estimateTextTokens(String(value));
42
+ }
43
+ const serialized = JSON.stringify(value);
44
+ return estimateTextTokens(serialized ?? "");
45
+ }
46
+ export function estimateAgentMessageTokens(message) {
47
+ return estimateSerializedTokens(message);
48
+ }
49
+ export function estimateAgentMessagesTokens(messages) {
50
+ return estimateSerializedTokens(messages);
51
+ }