@openviking/openclaw-plugin 2026.10.6 → 2026.10.7

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
@@ -1,5 +1,7 @@
1
1
  # OpenViking for OpenClaw
2
2
 
3
+ > **Working memory is now opt-in.** Update installed plugins separately from the OV server, then restart the host. Existing explicit settings still take precedence. See the [default-off upgrade guide](../../docs/en/guides/working-memory-default-off.md) for native history, re-enabling WM, and old-conversation handoffs.
4
+
3
5
  Use [OpenViking](https://github.com/volcengine/OpenViking) as OpenClaw's long-term context engine: automatic recall, session archive, memory extraction, semantic search, and RAG over a remote OpenViking server.
4
6
 
5
7
  ## Quick Start
@@ -243,7 +245,7 @@ Use it as a complement to auto-capture, not a replacement:
243
245
 
244
246
  - auto-capture still preserves ordinary conversation flow and batches extraction for cost and latency
245
247
  - `memory_store` is for explicit durable-memory intent such as "remember my main project is X" or "save this preference"
246
- - if `memory_store` commits but extracts 0 memories, check the OpenViking server extraction/model configuration; the explicit path triggered extraction, but the extractor did not produce a memory
248
+ - if `memory_store` commits but extracts 0 memories, it returns `action: "failed"` with `error: "no_memories_extracted"` and the agent tells the user no new memory was created; possible causes are that the content is already stored, extraction is disabled, or the extraction/model configuration needs checking
247
249
 
248
250
  ### What `compact()` does
249
251
 
package/client.ts CHANGED
@@ -75,6 +75,7 @@ export type OpenVikingClientOptions = {
75
75
  };
76
76
 
77
77
  export type CommitSessionResult = {
78
+ effective_enable_working_memory?: boolean;
78
79
  session_id: string;
79
80
  /** "accepted" (async), "skipped" (no archive), "completed", "failed", or "timeout" (wait mode). */
80
81
  status: string;
@@ -841,6 +842,7 @@ export class OpenVikingClient {
841
842
  sessionId: string,
842
843
  options?: {
843
844
  wait?: boolean;
845
+ enableWorkingMemory?: boolean;
844
846
  timeoutMs?: number;
845
847
  /**
846
848
  * WM v2: number of most-recent messages to keep live after commit.
@@ -871,6 +873,9 @@ export class OpenVikingClient {
871
873
  options?.agentId,
872
874
  );
873
875
  const body: Record<string, unknown> = {};
876
+ if (options?.enableWorkingMemory !== undefined) {
877
+ body.enable_working_memory = options.enableWorkingMemory;
878
+ }
874
879
  if (options?.retentionMode === "turn_budget") {
875
880
  body.retention_mode = "turn_budget";
876
881
  } else if (keepRecentCount > 0) {
package/commands/setup.ts CHANGED
@@ -66,6 +66,7 @@ function readCompatRangeFromManifest(): { min: string; max: string } {
66
66
  const PLUGIN_VERSION = readPluginVersion();
67
67
  const { min: COMPATIBLE_SERVER_MIN, max: COMPATIBLE_SERVER_MAX } = readCompatRangeFromManifest();
68
68
  const CONFIG_KEYS_TO_PRESERVE = [
69
+ "contextManagementMode",
69
70
  "targetUri",
70
71
  "headers",
71
72
  "timeoutMs",
package/config.ts CHANGED
@@ -42,6 +42,7 @@ export type MemoryOpenVikingConfig = {
42
42
  userId?: string;
43
43
  targetUri?: string;
44
44
  timeoutMs?: number;
45
+ contextManagementMode?: "native" | "openviking";
45
46
  autoCapture?: boolean;
46
47
  captureMode?: "semantic" | "keyword";
47
48
  captureMaxLength?: number;
@@ -514,6 +515,9 @@ export const memoryOpenVikingConfigSchema = {
514
515
  value = {};
515
516
  }
516
517
  const cfg = value as Record<string, unknown>;
518
+ if (cfg.contextManagementMode !== undefined && !["native", "openviking"].includes(String(cfg.contextManagementMode))) {
519
+ throw new Error("contextManagementMode must be native or openviking");
520
+ }
517
521
  assertAllowedKeys(
518
522
  cfg,
519
523
  [
@@ -528,6 +532,7 @@ export const memoryOpenVikingConfigSchema = {
528
532
  "userId",
529
533
  "targetUri",
530
534
  "timeoutMs",
535
+ "contextManagementMode",
531
536
  "autoCapture",
532
537
  "captureMode",
533
538
  "captureMaxLength",
@@ -655,6 +660,7 @@ export const memoryOpenVikingConfigSchema = {
655
660
  userId,
656
661
  targetUri: typeof cfg.targetUri === "string" ? cfg.targetUri : DEFAULT_TARGET_URI,
657
662
  timeoutMs: Math.max(1000, Math.floor(toNumber(cfg.timeoutMs, DEFAULT_TIMEOUT_MS))),
663
+ contextManagementMode: cfg.contextManagementMode === "openviking" ? "openviking" : "native",
658
664
  autoCapture: cfg.autoCapture !== false,
659
665
  captureMode: captureMode ?? DEFAULT_CAPTURE_MODE,
660
666
  captureMaxLength: Math.max(
package/context-engine.ts CHANGED
@@ -25,7 +25,7 @@ type ContextEngineInfo = {
25
25
  id: string;
26
26
  name: string;
27
27
  version?: string;
28
- ownsCompaction: true;
28
+ ownsCompaction: boolean;
29
29
  /** OpenClaw >=2026.8.1: without both declarations (and commitTurn) the engine is degraded to "legacy" every turn. */
30
30
  transcriptSemantics: {
31
31
  currentTurnFence: "before-current-turn-entry-v1";
@@ -365,7 +365,7 @@ export function createMemoryOpenVikingContextEngine(params: {
365
365
  id,
366
366
  name,
367
367
  version,
368
- ownsCompaction: true,
368
+ ownsCompaction: cfg.contextManagementMode === "openviking",
369
369
  transcriptSemantics: {
370
370
  currentTurnFence: "before-current-turn-entry-v1",
371
371
  turnAdvancementIdempotency: "atomic-idempotent-v1",
@@ -490,6 +490,7 @@ export function createMemoryOpenVikingContextEngine(params: {
490
490
  async compact(compactParams): Promise<CompactResult> {
491
491
  const tokenBudget = validTokenBudget(compactParams.tokenBudget) ?? 128_000;
492
492
  return compactOpenVikingSession({
493
+ contextManagementMode: cfg.contextManagementMode,
493
494
  sessionId: compactParams.sessionId,
494
495
  sessionKey: resolveSessionKey(compactParams),
495
496
  tokenBudget,
package/dist/client.js CHANGED
@@ -414,6 +414,9 @@ export class OpenVikingClient {
414
414
  retentionMode: options?.retentionMode,
415
415
  }, options?.agentId);
416
416
  const body = {};
417
+ if (options?.enableWorkingMemory !== undefined) {
418
+ body.enable_working_memory = options.enableWorkingMemory;
419
+ }
417
420
  if (options?.retentionMode === "turn_budget") {
418
421
  body.retention_mode = "turn_budget";
419
422
  }
@@ -56,6 +56,7 @@ function readCompatRangeFromManifest() {
56
56
  const PLUGIN_VERSION = readPluginVersion();
57
57
  const { min: COMPATIBLE_SERVER_MIN, max: COMPATIBLE_SERVER_MAX } = readCompatRangeFromManifest();
58
58
  const CONFIG_KEYS_TO_PRESERVE = [
59
+ "contextManagementMode",
59
60
  "targetUri",
60
61
  "headers",
61
62
  "timeoutMs",
package/dist/config.js CHANGED
@@ -328,6 +328,9 @@ export const memoryOpenVikingConfigSchema = {
328
328
  value = {};
329
329
  }
330
330
  const cfg = value;
331
+ if (cfg.contextManagementMode !== undefined && !["native", "openviking"].includes(String(cfg.contextManagementMode))) {
332
+ throw new Error("contextManagementMode must be native or openviking");
333
+ }
331
334
  assertAllowedKeys(cfg, [
332
335
  "mode",
333
336
  "baseUrl",
@@ -340,6 +343,7 @@ export const memoryOpenVikingConfigSchema = {
340
343
  "userId",
341
344
  "targetUri",
342
345
  "timeoutMs",
346
+ "contextManagementMode",
343
347
  "autoCapture",
344
348
  "captureMode",
345
349
  "captureMaxLength",
@@ -436,6 +440,7 @@ export const memoryOpenVikingConfigSchema = {
436
440
  userId,
437
441
  targetUri: typeof cfg.targetUri === "string" ? cfg.targetUri : DEFAULT_TARGET_URI,
438
442
  timeoutMs: Math.max(1000, Math.floor(toNumber(cfg.timeoutMs, DEFAULT_TIMEOUT_MS))),
443
+ contextManagementMode: cfg.contextManagementMode === "openviking" ? "openviking" : "native",
439
444
  autoCapture: cfg.autoCapture !== false,
440
445
  captureMode: captureMode ?? DEFAULT_CAPTURE_MODE,
441
446
  captureMaxLength: Math.max(200, Math.min(200_000, Math.floor(toNumber(cfg.captureMaxLength, DEFAULT_CAPTURE_MAX_LENGTH)))),
@@ -191,7 +191,7 @@ export function createMemoryOpenVikingContextEngine(params) {
191
191
  id,
192
192
  name,
193
193
  version,
194
- ownsCompaction: true,
194
+ ownsCompaction: cfg.contextManagementMode === "openviking",
195
195
  transcriptSemantics: {
196
196
  currentTurnFence: "before-current-turn-entry-v1",
197
197
  turnAdvancementIdempotency: "atomic-idempotent-v1",
@@ -308,6 +308,7 @@ export function createMemoryOpenVikingContextEngine(params) {
308
308
  async compact(compactParams) {
309
309
  const tokenBudget = validTokenBudget(compactParams.tokenBudget) ?? 128_000;
310
310
  return compactOpenVikingSession({
311
+ contextManagementMode: cfg.contextManagementMode,
311
312
  sessionId: compactParams.sessionId,
312
313
  sessionKey: resolveSessionKey(compactParams),
313
314
  tokenBudget,
@@ -95,13 +95,31 @@ export function registerOpenVikingMemoryTools(deps) {
95
95
  };
96
96
  }
97
97
  if (memoriesCount === 0) {
98
- deps.logger.warn(`openviking: memory_store committed but 0 memories extracted (sessionId=${sessionId}). ` +
99
- "Check OpenViking server logs for embedding/extract errors (e.g. 401 API key, or extraction pipeline).");
100
- }
101
- else {
102
- deps.logger.info?.(`openviking: memory_store committed, memories=${memoriesCount}, ` +
103
- `trace_id=${commitResult.trace_id ?? "none"}`);
98
+ deps.logger.warn(`openviking: memory_store committed but 0 memories extracted (sessionId=${sessionId}), ` +
99
+ `trace_id=${commitResult.trace_id ?? "none"}. ` +
100
+ "No memory was created or updated. Check memory.extraction_enabled, VLM configuration/API keys, or whether the content is already stored.");
101
+ return {
102
+ content: [{
103
+ type: "text",
104
+ text: `Memory extraction completed for session ${sessionId}, but created or updated no memory. ` +
105
+ "The content may already be stored, memory extraction may be disabled, or the extractor found nothing new to store. " +
106
+ "Tell the user no new memory was created." +
107
+ (commitResult.trace_id ? ` (trace_id=${commitResult.trace_id})` : ""),
108
+ }],
109
+ details: {
110
+ action: "failed",
111
+ sessionId,
112
+ status: commitResult.status,
113
+ error: "no_memories_extracted",
114
+ memoriesCount,
115
+ archived: commitResult.archived ?? false,
116
+ traceId: commitResult.trace_id,
117
+ usedTempSession,
118
+ },
119
+ };
104
120
  }
121
+ deps.logger.info?.(`openviking: memory_store committed, memories=${memoriesCount}, ` +
122
+ `trace_id=${commitResult.trace_id ?? "none"}`);
105
123
  return {
106
124
  content: [
107
125
  {
@@ -344,12 +344,23 @@ async function assembleSessionContext(params) {
344
344
  });
345
345
  }
346
346
  }
347
+ if (cfg.contextManagementMode !== "openviking") {
348
+ return assemblePassthrough({ diag, ovSessionId, reason: "native_context", liveMessages: messages, originalTokens });
349
+ }
347
350
  try {
348
351
  const client = await getClient();
349
352
  const ctx = await client.getSessionContext(ovSessionId, tokenBudget);
350
353
  const preAbstracts = ctx?.pre_archive_abstracts ?? [];
351
354
  const hasArchives = !!ctx?.latest_archive_overview || preAbstracts.length > 0;
352
355
  const activeCount = ctx?.messages?.length ?? 0;
356
+ // A completed WM-off archive can leave a non-empty active tail. That tail
357
+ // alone cannot replace the host's earlier history in explicit OV mode.
358
+ if (!hasArchives && (ctx?.stats?.totalArchives ?? 0) > 0) {
359
+ logger.warn?.("openviking: archived context has no summary; keeping native history");
360
+ return assemblePassthrough({
361
+ diag, ovSessionId, reason: "archive_summary_missing", liveMessages: messages, originalTokens,
362
+ });
363
+ }
353
364
  if (!ctx || (!hasArchives && activeCount === 0)) {
354
365
  return assemblePassthrough({
355
366
  diag,
@@ -658,6 +669,7 @@ export async function afterTurnOpenVikingSession({ throwOnError = false, session
658
669
  }
659
670
  const commitResult = await client.commitSession(ovSessionId, {
660
671
  wait: false,
672
+ ...(cfg.contextManagementMode === "openviking" ? { enableWorkingMemory: true } : {}),
661
673
  ...(cfg.commitRetentionMode === "turn_budget"
662
674
  ? { retentionMode: "turn_budget" }
663
675
  : { keepRecentCount: cfg.commitKeepRecentCount }),
@@ -666,6 +678,7 @@ export async function afterTurnOpenVikingSession({ throwOnError = false, session
666
678
  `status=${commitResult.status}, archived=${commitResult.archived ?? false}, ` +
667
679
  `task_id=${commitResult.task_id ?? "none"}, trace_id=${commitResult.trace_id ?? "none"}`);
668
680
  diag("afterTurn_commit", ovSessionId, {
681
+ effectiveEnableWorkingMemory: commitResult.effective_enable_working_memory,
669
682
  pendingTokens,
670
683
  commitTokenThreshold,
671
684
  commitTokenThresholdRatio: cfg.commitTokenThresholdRatio,
@@ -715,7 +728,7 @@ function compactFailureResult(reason, tokensBefore, details) {
715
728
  },
716
729
  };
717
730
  }
718
- export async function compactOpenVikingSession({ sessionId, sessionKey, tokenBudget, currentTokenCount, force, compactionTarget, customInstructions, getClient, logger, resolveAgentId, isBypassedSession, runtimeCompact, diag, }) {
731
+ export async function compactOpenVikingSession({ contextManagementMode = "native", sessionId, sessionKey, tokenBudget, currentTokenCount, force, compactionTarget, customInstructions, getClient, logger, resolveAgentId, isBypassedSession, runtimeCompact, diag, }) {
719
732
  const ovSessionId = openClawSessionToOvStorageId(sessionId, sessionKey);
720
733
  diag("compact_entry", ovSessionId, {
721
734
  tokenBudget,
@@ -749,6 +762,9 @@ export async function compactOpenVikingSession({ sessionId, sessionKey, tokenBud
749
762
  reason: "session_bypassed",
750
763
  };
751
764
  }
765
+ if (contextManagementMode === "native") {
766
+ return await runtimeCompact?.() ?? compactFailureResult("native_compaction_unavailable", validTokenCount(currentTokenCount) ?? -1, { message: "Upgrade OpenClaw to a version with runtime compaction delegation." });
767
+ }
752
768
  const client = await getClient();
753
769
  const agentId = resolveAgentId(sessionId, sessionKey, ovSessionId);
754
770
  const tokensBeforeOriginal = validTokenCount(currentTokenCount);
@@ -771,6 +787,7 @@ export async function compactOpenVikingSession({ sessionId, sessionKey, tokenBud
771
787
  const commitResult = await client.commitSession(ovSessionId, {
772
788
  wait: true,
773
789
  keepRecentCount: 0,
790
+ enableWorkingMemory: true,
774
791
  });
775
792
  const memCount = totalExtractedMemories(commitResult.memories_extracted);
776
793
  if (commitResult.status === "failed") {
@@ -828,6 +845,9 @@ export async function compactOpenVikingSession({ sessionId, sessionKey, tokenBud
828
845
  },
829
846
  };
830
847
  }
848
+ if (commitResult.effective_enable_working_memory !== true) {
849
+ return await runtimeCompact?.() ?? compactFailureResult("working_memory_disabled", tokensBefore, { commit: commitResult });
850
+ }
831
851
  let summary = "";
832
852
  const firstKeptEntryId = commitResult.archive_uri?.split("/").pop() ?? "";
833
853
  let tokensAfter;
@@ -875,6 +895,9 @@ export async function compactOpenVikingSession({ sessionId, sessionKey, tokenBud
875
895
  logger.info(`openviking: compact tokens session=${ovSessionId}, ` +
876
896
  `tokensBefore=${tokensBefore}, tokensAfter=${tokensAfter ?? "unknown"}, ` +
877
897
  `latestArchiveId=${firstKeptEntryId || "none"}`);
898
+ if (!summary) {
899
+ return await runtimeCompact?.() ?? compactFailureResult("archive_summary_missing", tokensBefore, { commit: commitResult });
900
+ }
878
901
  diag("compact_result", ovSessionId, {
879
902
  ok: true,
880
903
  compacted: true,
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "manifestVersion": "1.0",
3
- "pluginVersion": "2026.9.21",
3
+ "pluginVersion": "2026.9.22",
4
4
  "plugin": {
5
5
  "id": "openviking",
6
6
  "kind": "context-engine",
@@ -353,6 +353,12 @@
353
353
  "timeoutMs": {
354
354
  "type": "number"
355
355
  },
356
+ "contextManagementMode": {
357
+ "type": "string",
358
+ "enum": ["native", "openviking"],
359
+ "default": "native",
360
+ "description": "Use host history and compaction; openviking explicitly enables Working Memory context management."
361
+ },
356
362
  "autoCapture": {
357
363
  "type": "boolean"
358
364
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openviking/openclaw-plugin",
3
- "version": "2026.10.6",
3
+ "version": "2026.10.7",
4
4
  "displayName": "OpenViking",
5
5
  "description": "OpenClaw context-engine plugin — OpenViking AI memory, long-term memory, knowledge base, semantic search, RAG, context engine. 长期记忆 · 知识库 · 语义检索 · 上下文引擎",
6
6
  "type": "module",
@@ -176,15 +176,34 @@ export function registerOpenVikingMemoryTools(deps: OpenVikingMemoryToolsDeps):
176
176
  }
177
177
  if (memoriesCount === 0) {
178
178
  deps.logger.warn(
179
- `openviking: memory_store committed but 0 memories extracted (sessionId=${sessionId}). ` +
180
- "Check OpenViking server logs for embedding/extract errors (e.g. 401 API key, or extraction pipeline).",
181
- );
182
- } else {
183
- deps.logger.info?.(
184
- `openviking: memory_store committed, memories=${memoriesCount}, ` +
185
- `trace_id=${commitResult.trace_id ?? "none"}`,
179
+ `openviking: memory_store committed but 0 memories extracted (sessionId=${sessionId}), ` +
180
+ `trace_id=${commitResult.trace_id ?? "none"}. ` +
181
+ "No memory was created or updated. Check memory.extraction_enabled, VLM configuration/API keys, or whether the content is already stored.",
186
182
  );
183
+ return {
184
+ content: [{
185
+ type: "text",
186
+ text: `Memory extraction completed for session ${sessionId}, but created or updated no memory. ` +
187
+ "The content may already be stored, memory extraction may be disabled, or the extractor found nothing new to store. " +
188
+ "Tell the user no new memory was created." +
189
+ (commitResult.trace_id ? ` (trace_id=${commitResult.trace_id})` : ""),
190
+ }],
191
+ details: {
192
+ action: "failed",
193
+ sessionId,
194
+ status: commitResult.status,
195
+ error: "no_memories_extracted",
196
+ memoriesCount,
197
+ archived: commitResult.archived ?? false,
198
+ traceId: commitResult.trace_id,
199
+ usedTempSession,
200
+ },
201
+ };
187
202
  }
203
+ deps.logger.info?.(
204
+ `openviking: memory_store committed, memories=${memoriesCount}, ` +
205
+ `trace_id=${commitResult.trace_id ?? "none"}`,
206
+ );
188
207
  return {
189
208
  content: [
190
209
  {
@@ -107,6 +107,7 @@ export type AssembleOpenVikingSessionParams = {
107
107
  type CompactClient = Pick<OpenVikingClient, "commitSession" | "getSessionContext">;
108
108
 
109
109
  export type CompactOpenVikingSessionParams = {
110
+ contextManagementMode?: "native" | "openviking";
110
111
  sessionId: string;
111
112
  sessionKey?: string;
112
113
  tokenBudget: number;
@@ -138,6 +139,7 @@ export type AfterTurnOpenVikingSessionParams = {
138
139
  runtimeContext?: Record<string, unknown>;
139
140
  cfg: {
140
141
  autoCapture: boolean;
142
+ contextManagementMode?: "native" | "openviking";
141
143
  commitTokenThresholdRatio: number;
142
144
  commitKeepRecentCount: number;
143
145
  commitRetentionMode?: "message_count" | "turn_budget";
@@ -610,6 +612,10 @@ async function assembleSessionContext(params: AssembleOpenVikingSessionParams):
610
612
  }
611
613
  }
612
614
 
615
+ if (cfg.contextManagementMode !== "openviking") {
616
+ return assemblePassthrough({ diag, ovSessionId, reason: "native_context", liveMessages: messages, originalTokens });
617
+ }
618
+
613
619
  try {
614
620
  const client = await getClient();
615
621
  const ctx = await client.getSessionContext(ovSessionId, tokenBudget);
@@ -618,6 +624,15 @@ async function assembleSessionContext(params: AssembleOpenVikingSessionParams):
618
624
  const hasArchives = !!ctx?.latest_archive_overview || preAbstracts.length > 0;
619
625
  const activeCount = ctx?.messages?.length ?? 0;
620
626
 
627
+ // A completed WM-off archive can leave a non-empty active tail. That tail
628
+ // alone cannot replace the host's earlier history in explicit OV mode.
629
+ if (!hasArchives && (ctx?.stats?.totalArchives ?? 0) > 0) {
630
+ logger.warn?.("openviking: archived context has no summary; keeping native history");
631
+ return assemblePassthrough({
632
+ diag, ovSessionId, reason: "archive_summary_missing", liveMessages: messages, originalTokens,
633
+ });
634
+ }
635
+
621
636
  if (!ctx || (!hasArchives && activeCount === 0)) {
622
637
  return assemblePassthrough({
623
638
  diag,
@@ -989,6 +1004,7 @@ export async function afterTurnOpenVikingSession({
989
1004
 
990
1005
  const commitResult = await client.commitSession(ovSessionId, {
991
1006
  wait: false,
1007
+ ...(cfg.contextManagementMode === "openviking" ? { enableWorkingMemory: true } : {}),
992
1008
  ...(cfg.commitRetentionMode === "turn_budget"
993
1009
  ? { retentionMode: "turn_budget" as const }
994
1010
  : { keepRecentCount: cfg.commitKeepRecentCount }),
@@ -1000,6 +1016,7 @@ export async function afterTurnOpenVikingSession({
1000
1016
  );
1001
1017
 
1002
1018
  diag("afterTurn_commit", ovSessionId, {
1019
+ effectiveEnableWorkingMemory: commitResult.effective_enable_working_memory,
1003
1020
  pendingTokens,
1004
1021
  commitTokenThreshold,
1005
1022
  commitTokenThresholdRatio: cfg.commitTokenThresholdRatio,
@@ -1058,6 +1075,7 @@ function compactFailureResult(
1058
1075
  }
1059
1076
 
1060
1077
  export async function compactOpenVikingSession({
1078
+ contextManagementMode = "native",
1061
1079
  sessionId,
1062
1080
  sessionKey,
1063
1081
  tokenBudget,
@@ -1107,6 +1125,13 @@ export async function compactOpenVikingSession({
1107
1125
  };
1108
1126
  }
1109
1127
 
1128
+ if (contextManagementMode === "native") {
1129
+ return await runtimeCompact?.() ?? compactFailureResult(
1130
+ "native_compaction_unavailable", validTokenCount(currentTokenCount) ?? -1,
1131
+ { message: "Upgrade OpenClaw to a version with runtime compaction delegation." },
1132
+ );
1133
+ }
1134
+
1110
1135
  const client = await getClient();
1111
1136
  const agentId = resolveAgentId(sessionId, sessionKey, ovSessionId);
1112
1137
  const tokensBeforeOriginal = validTokenCount(currentTokenCount);
@@ -1134,6 +1159,7 @@ export async function compactOpenVikingSession({
1134
1159
  const commitResult = await client.commitSession(ovSessionId, {
1135
1160
  wait: true,
1136
1161
  keepRecentCount: 0,
1162
+ enableWorkingMemory: true,
1137
1163
  });
1138
1164
  const memCount = totalExtractedMemories(commitResult.memories_extracted);
1139
1165
 
@@ -1204,6 +1230,10 @@ export async function compactOpenVikingSession({
1204
1230
  };
1205
1231
  }
1206
1232
 
1233
+ if (commitResult.effective_enable_working_memory !== true) {
1234
+ return await runtimeCompact?.() ?? compactFailureResult("working_memory_disabled", tokensBefore, { commit: commitResult });
1235
+ }
1236
+
1207
1237
  let summary = "";
1208
1238
  const firstKeptEntryId = commitResult.archive_uri?.split("/").pop() ?? "";
1209
1239
  let tokensAfter: number | undefined;
@@ -1264,6 +1294,10 @@ export async function compactOpenVikingSession({
1264
1294
  `latestArchiveId=${firstKeptEntryId || "none"}`,
1265
1295
  );
1266
1296
 
1297
+ if (!summary) {
1298
+ return await runtimeCompact?.() ?? compactFailureResult("archive_summary_missing", tokensBefore, { commit: commitResult });
1299
+ }
1300
+
1267
1301
  diag("compact_result", ovSessionId, {
1268
1302
  ok: true,
1269
1303
  compacted: true,
@@ -30,7 +30,7 @@ Choose the registered names that match the current runtime:
30
30
  | Runtime | Search | Read |
31
31
  | --- | --- | --- |
32
32
  | OpenViking MCP, Codex, Claude Code | `find` or `search` | `read` |
33
- | OpenCode | `openviking_find` or `openviking_search` | `openviking_read` |
33
+ | OpenCode, pi | `openviking_find` or `openviking_search` | `openviking_read` |
34
34
  | OpenClaw | `ov_search` | `ov_read` or `ov_multi_read` |
35
35
 
36
36
  The host may display MCP names with a namespace such as