@openviking/openclaw-plugin 2026.8.21 → 2026.8.30
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/context-engine.ts
CHANGED
|
@@ -25,6 +25,11 @@ type ContextEngineInfo = {
|
|
|
25
25
|
name: string;
|
|
26
26
|
version?: string;
|
|
27
27
|
ownsCompaction: true;
|
|
28
|
+
/** OpenClaw >=2026.8.1: without both declarations (and commitTurn) the engine is degraded to "legacy" every turn. */
|
|
29
|
+
transcriptSemantics: {
|
|
30
|
+
currentTurnFence: "before-current-turn-entry-v1";
|
|
31
|
+
turnAdvancementIdempotency: "atomic-idempotent-v1";
|
|
32
|
+
};
|
|
28
33
|
};
|
|
29
34
|
|
|
30
35
|
type AssembleResult = {
|
|
@@ -81,6 +86,14 @@ type ContextEngine = {
|
|
|
81
86
|
tokenBudget?: number;
|
|
82
87
|
runtimeContext?: Record<string, unknown>;
|
|
83
88
|
}) => Promise<AssembleResult>;
|
|
89
|
+
/** OpenClaw >=2026.8.1 durable turn advancement; retried with the same advancementKey after host failure. */
|
|
90
|
+
commitTurn: (params: {
|
|
91
|
+
advancementKey: string;
|
|
92
|
+
messages: AgentMessage[];
|
|
93
|
+
sessionId: string;
|
|
94
|
+
sessionKey?: string;
|
|
95
|
+
isHeartbeat?: boolean;
|
|
96
|
+
}) => Promise<{ status: "committed" | "duplicate" }>;
|
|
84
97
|
compact: (params: {
|
|
85
98
|
sessionId: string;
|
|
86
99
|
sessionKey?: string;
|
|
@@ -318,12 +331,18 @@ export function createMemoryOpenVikingContextEngine(params: {
|
|
|
318
331
|
};
|
|
319
332
|
}
|
|
320
333
|
|
|
334
|
+
const committedTurnKeys = new Set<string>();
|
|
335
|
+
|
|
321
336
|
return {
|
|
322
337
|
info: {
|
|
323
338
|
id,
|
|
324
339
|
name,
|
|
325
340
|
version,
|
|
326
341
|
ownsCompaction: true,
|
|
342
|
+
transcriptSemantics: {
|
|
343
|
+
currentTurnFence: "before-current-turn-entry-v1",
|
|
344
|
+
turnAdvancementIdempotency: "atomic-idempotent-v1",
|
|
345
|
+
},
|
|
327
346
|
},
|
|
328
347
|
|
|
329
348
|
commitOVSession: doCommitOVSession,
|
|
@@ -368,6 +387,22 @@ export function createMemoryOpenVikingContextEngine(params: {
|
|
|
368
387
|
});
|
|
369
388
|
},
|
|
370
389
|
|
|
390
|
+
// Capture still happens in afterTurn (host calls it per LLM call + on finalize);
|
|
391
|
+
// commitTurn only acknowledges the accepted turn so OpenClaw drains its outbox.
|
|
392
|
+
// ponytail: in-memory key set, not durable across restarts — the host outbox is.
|
|
393
|
+
async commitTurn({ advancementKey, sessionId }): Promise<{ status: "committed" | "duplicate" }> {
|
|
394
|
+
if (committedTurnKeys.has(advancementKey)) {
|
|
395
|
+
diag("commitTurn_duplicate", sessionId, { advancementKey });
|
|
396
|
+
return { status: "duplicate" };
|
|
397
|
+
}
|
|
398
|
+
committedTurnKeys.add(advancementKey);
|
|
399
|
+
if (committedTurnKeys.size > 1024) {
|
|
400
|
+
committedTurnKeys.delete(committedTurnKeys.values().next().value as string);
|
|
401
|
+
}
|
|
402
|
+
diag("commitTurn", sessionId, { advancementKey });
|
|
403
|
+
return { status: "committed" };
|
|
404
|
+
},
|
|
405
|
+
|
|
371
406
|
async afterTurn(afterTurnParams): Promise<void> {
|
|
372
407
|
const tokenBudget = validTokenBudget(afterTurnParams.tokenBudget) ?? 128_000;
|
|
373
408
|
await afterTurnOpenVikingSession({
|
package/dist/context-engine.js
CHANGED
|
@@ -159,12 +159,17 @@ export function createMemoryOpenVikingContextEngine(params) {
|
|
|
159
159
|
ovSessionId: openClawSessionToOvStorageId(params.sessionId, sessionKey),
|
|
160
160
|
};
|
|
161
161
|
}
|
|
162
|
+
const committedTurnKeys = new Set();
|
|
162
163
|
return {
|
|
163
164
|
info: {
|
|
164
165
|
id,
|
|
165
166
|
name,
|
|
166
167
|
version,
|
|
167
168
|
ownsCompaction: true,
|
|
169
|
+
transcriptSemantics: {
|
|
170
|
+
currentTurnFence: "before-current-turn-entry-v1",
|
|
171
|
+
turnAdvancementIdempotency: "atomic-idempotent-v1",
|
|
172
|
+
},
|
|
168
173
|
},
|
|
169
174
|
commitOVSession: doCommitOVSession,
|
|
170
175
|
// --- standard ContextEngine methods ---
|
|
@@ -202,6 +207,21 @@ export function createMemoryOpenVikingContextEngine(params) {
|
|
|
202
207
|
prependRecallToLatestUserMessage,
|
|
203
208
|
});
|
|
204
209
|
},
|
|
210
|
+
// Capture still happens in afterTurn (host calls it per LLM call + on finalize);
|
|
211
|
+
// commitTurn only acknowledges the accepted turn so OpenClaw drains its outbox.
|
|
212
|
+
// ponytail: in-memory key set, not durable across restarts — the host outbox is.
|
|
213
|
+
async commitTurn({ advancementKey, sessionId }) {
|
|
214
|
+
if (committedTurnKeys.has(advancementKey)) {
|
|
215
|
+
diag("commitTurn_duplicate", sessionId, { advancementKey });
|
|
216
|
+
return { status: "duplicate" };
|
|
217
|
+
}
|
|
218
|
+
committedTurnKeys.add(advancementKey);
|
|
219
|
+
if (committedTurnKeys.size > 1024) {
|
|
220
|
+
committedTurnKeys.delete(committedTurnKeys.values().next().value);
|
|
221
|
+
}
|
|
222
|
+
diag("commitTurn", sessionId, { advancementKey });
|
|
223
|
+
return { status: "committed" };
|
|
224
|
+
},
|
|
205
225
|
async afterTurn(afterTurnParams) {
|
|
206
226
|
const tokenBudget = validTokenBudget(afterTurnParams.tokenBudget) ?? 128_000;
|
|
207
227
|
await afterTurnOpenVikingSession({
|
|
@@ -196,10 +196,14 @@ function isSessionNotFoundError(err) {
|
|
|
196
196
|
return errorMessage.includes("[NOT_FOUND]") && errorMessage.includes("Session not found");
|
|
197
197
|
}
|
|
198
198
|
export async function assembleOpenVikingSession({ sessionId, sessionKey, messages, tokenBudget, runtimeContext, isMainAssemble, cfg, getClient, logger, resolveAgentId, rememberSessionAgentId, isBypassedSession, queryConfigStore, traceRecorder, diag, roughEstimate, messageDigest, extractAgentMessageText, hasAutoRecallBlock, prependRecallToLatestUserMessage, }) {
|
|
199
|
-
const ovSessionId = openClawSessionToOvStorageId(sessionId, sessionKey);
|
|
200
|
-
const sender = extractRuntimeSenderId(runtimeContext);
|
|
201
199
|
const latestMessage = messages.at(-1);
|
|
202
200
|
const isTransformContextAssemble = !isMainAssemble;
|
|
201
|
+
// Tool-loop calls do no recall work on non-user tails; avoid scanning the full history.
|
|
202
|
+
if (isTransformContextAssemble && latestMessage?.role !== "user") {
|
|
203
|
+
return { messages, estimatedTokens: 0 };
|
|
204
|
+
}
|
|
205
|
+
const ovSessionId = openClawSessionToOvStorageId(sessionId, sessionKey);
|
|
206
|
+
const sender = extractRuntimeSenderId(runtimeContext);
|
|
203
207
|
const originalTokens = roughEstimate(messages);
|
|
204
208
|
rememberSessionAgentId?.({
|
|
205
209
|
sessionId,
|
|
@@ -220,16 +224,6 @@ export async function assembleOpenVikingSession({ sessionId, sessionKey, message
|
|
|
220
224
|
return assemblePassthrough({ diag, ovSessionId, reason: "session_bypassed", liveMessages: messages, originalTokens });
|
|
221
225
|
}
|
|
222
226
|
if (isTransformContextAssemble) {
|
|
223
|
-
if (latestMessage?.role !== "user") {
|
|
224
|
-
return assemblePassthrough({
|
|
225
|
-
diag,
|
|
226
|
-
ovSessionId,
|
|
227
|
-
reason: "transform_context_non_user_tail",
|
|
228
|
-
liveMessages: messages,
|
|
229
|
-
originalTokens,
|
|
230
|
-
extra: { latestRole: latestMessage?.role ?? null },
|
|
231
|
-
});
|
|
232
|
-
}
|
|
233
227
|
if (!cfg.autoRecall) {
|
|
234
228
|
return assemblePassthrough({ diag, ovSessionId, reason: "transform_context_auto_recall_disabled", liveMessages: messages, originalTokens });
|
|
235
229
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openviking/openclaw-plugin",
|
|
3
|
-
"version": "2026.8.
|
|
3
|
+
"version": "2026.8.30",
|
|
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",
|
|
@@ -441,10 +441,16 @@ export async function assembleOpenVikingSession({
|
|
|
441
441
|
hasAutoRecallBlock,
|
|
442
442
|
prependRecallToLatestUserMessage,
|
|
443
443
|
}: AssembleOpenVikingSessionParams): Promise<AssembleOpenVikingSessionResult> {
|
|
444
|
-
const ovSessionId = openClawSessionToOvStorageId(sessionId, sessionKey);
|
|
445
|
-
const sender = extractRuntimeSenderId(runtimeContext);
|
|
446
444
|
const latestMessage = messages.at(-1);
|
|
447
445
|
const isTransformContextAssemble = !isMainAssemble;
|
|
446
|
+
|
|
447
|
+
// Tool-loop calls do no recall work on non-user tails; avoid scanning the full history.
|
|
448
|
+
if (isTransformContextAssemble && latestMessage?.role !== "user") {
|
|
449
|
+
return { messages, estimatedTokens: 0 };
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
const ovSessionId = openClawSessionToOvStorageId(sessionId, sessionKey);
|
|
453
|
+
const sender = extractRuntimeSenderId(runtimeContext);
|
|
448
454
|
const originalTokens = roughEstimate(messages);
|
|
449
455
|
|
|
450
456
|
rememberSessionAgentId?.({
|
|
@@ -468,16 +474,6 @@ export async function assembleOpenVikingSession({
|
|
|
468
474
|
}
|
|
469
475
|
|
|
470
476
|
if (isTransformContextAssemble) {
|
|
471
|
-
if (latestMessage?.role !== "user") {
|
|
472
|
-
return assemblePassthrough({
|
|
473
|
-
diag,
|
|
474
|
-
ovSessionId,
|
|
475
|
-
reason: "transform_context_non_user_tail",
|
|
476
|
-
liveMessages: messages,
|
|
477
|
-
originalTokens,
|
|
478
|
-
extra: { latestRole: latestMessage?.role ?? null },
|
|
479
|
-
});
|
|
480
|
-
}
|
|
481
477
|
if (!cfg.autoRecall) {
|
|
482
478
|
return assemblePassthrough({ diag, ovSessionId, reason: "transform_context_auto_recall_disabled", liveMessages: messages, originalTokens });
|
|
483
479
|
}
|