@openviking/openclaw-plugin 2026.6.18 → 2026.7.15
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/INSTALL-AGENT.md +2 -2
- package/INSTALL-ZH.md +2 -2
- package/INSTALL.md +2 -2
- package/README.md +1 -1
- package/auto-recall.ts +6 -6
- package/client.ts +14 -6
- package/commands/setup.ts +40 -10
- package/config.ts +44 -8
- package/context-engine.ts +2 -1
- package/dist/auto-recall.js +5 -6
- package/dist/client.js +11 -4
- package/dist/commands/setup.js +26 -10
- package/dist/config.js +31 -6
- package/dist/context-engine.js +2 -1
- package/dist/index.js +4 -4
- package/dist/plugin/openviking-archive-tools.js +1 -2
- package/dist/plugin/openviking-client-runtime.js +7 -1
- package/dist/plugin/openviking-command-definitions.js +3 -3
- package/dist/plugin/openviking-import-tools.js +2 -2
- package/dist/plugin/openviking-memory-recall-tools.js +4 -4
- package/dist/plugin/openviking-memory-tools.js +12 -7
- package/dist/plugin/openviking-query-runtime.js +1 -1
- package/dist/plugin/openviking-query-tools.js +4 -4
- package/dist/plugin/openviking-recall-trace-runtime.js +3 -3
- package/dist/plugin/openviking-session-routing-runtime.js +9 -2
- package/dist/plugin/openviking-tool-result-tools.js +3 -3
- package/dist/query-config.js +7 -1
- package/dist/request-headers.js +19 -0
- package/dist/routing/identity-routing.js +27 -0
- package/dist/routing/memory-uri.js +1 -0
- package/dist/services/context-lifecycle-service.js +56 -23
- package/dist/services/context-message-adapter.js +2 -9
- package/dist/text-utils.js +1 -6
- package/index.ts +3 -3
- package/install-manifest.json +2 -1
- package/openclaw.plugin.json +20 -4
- package/package.json +6 -6
- package/plugin/command-registration.ts +2 -0
- package/plugin/openviking-archive-tools.ts +1 -4
- package/plugin/openviking-client-runtime.ts +8 -1
- package/plugin/openviking-command-definitions.ts +4 -3
- package/plugin/openviking-import-tools.ts +4 -2
- package/plugin/openviking-memory-recall-tools.ts +6 -4
- package/plugin/openviking-memory-tools.ts +19 -11
- package/plugin/openviking-query-runtime.ts +1 -1
- package/plugin/openviking-query-tools.ts +6 -4
- package/plugin/openviking-recall-trace-runtime.ts +4 -3
- package/plugin/openviking-recall-trace-tools.ts +2 -0
- package/plugin/openviking-session-routing-runtime.ts +15 -1
- package/plugin/openviking-tool-result-tools.ts +0 -6
- package/query-config.ts +7 -1
- package/request-headers.ts +29 -0
- package/routing/identity-routing.ts +41 -0
- package/routing/memory-uri.ts +1 -0
- package/services/context-lifecycle-service.ts +69 -25
- package/services/context-message-adapter.ts +2 -9
- package/services/setup/probe-service.ts +35 -6
- package/services/setup/setup-flow.ts +61 -7
- package/skills/install-openviking-memory/SKILL.md +29 -15
- package/skills/openviking-context-database/SKILL.md +3 -3
- package/text-utils.ts +1 -6
|
@@ -3,6 +3,33 @@ const DEFAULT_OPENCLAW_AGENT_ID = "main";
|
|
|
3
3
|
/** OpenClaw session UUID (path-safe on Windows). */
|
|
4
4
|
const OPENVIKING_OV_SESSION_UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
5
5
|
const WINDOWS_BAD_SESSION_SEGMENT = /[:<>"\\/|?\u0000-\u001f]/;
|
|
6
|
+
export function sanitizeOpenVikingPeerId(raw) {
|
|
7
|
+
const normalized = raw
|
|
8
|
+
?.trim()
|
|
9
|
+
.replace(/[^a-zA-Z0-9_-]+/g, "_")
|
|
10
|
+
.replace(/^_+|_+$/g, "")
|
|
11
|
+
.replace(/_+/g, "_");
|
|
12
|
+
return normalized || undefined;
|
|
13
|
+
}
|
|
14
|
+
export function resolveOpenVikingMessagePeerId(params) {
|
|
15
|
+
if (params.peerRole === "person" && params.role === "user") {
|
|
16
|
+
return params.personPeerId;
|
|
17
|
+
}
|
|
18
|
+
if (params.peerRole === "assistant" && params.role === "assistant") {
|
|
19
|
+
return params.assistantPeerId;
|
|
20
|
+
}
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
export function resolveOpenVikingActorPeerId(params) {
|
|
24
|
+
const actorPeerId = resolveOpenVikingMessagePeerId({
|
|
25
|
+
...params,
|
|
26
|
+
role: params.peerRole === "person" ? "user" : "assistant",
|
|
27
|
+
});
|
|
28
|
+
if (params.peerRole === "person" && !actorPeerId) {
|
|
29
|
+
throw new Error("openviking: peer_role=person requires a sender identity");
|
|
30
|
+
}
|
|
31
|
+
return actorPeerId;
|
|
32
|
+
}
|
|
6
33
|
/**
|
|
7
34
|
* Map OpenClaw session identity to an OpenViking session_id that is safe as a single
|
|
8
35
|
* AGFS path segment on Windows (no `:` etc.). Prefer UUID sessionId when present;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const MEMORY_URI_PATTERNS = [
|
|
2
2
|
/^viking:\/\/user\/(?:[^/]+(?:\/agent\/[^/]+)?\/)?memories(?:\/|$)/,
|
|
3
|
+
/^viking:\/\/user\/[^/]+\/peers\/[^/]+\/memories(?:\/|$)/,
|
|
3
4
|
/^viking:\/\/agent\/(?:[^/]+(?:\/user\/[^/]+)?\/)?memories(?:\/|$)/,
|
|
4
5
|
];
|
|
5
6
|
export function isMemoryUri(uri) {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { DEFAULT_PHASE2_POLL_TIMEOUT_MS } from "../client.js";
|
|
3
3
|
import { buildAutoRecallContext, prepareRecallQuery } from "../auto-recall.js";
|
|
4
4
|
import { toJsonLog } from "../memory-ranking.js";
|
|
5
|
-
import { openClawSessionToOvStorageId } from "../routing/identity-routing.js";
|
|
5
|
+
import { openClawSessionToOvStorageId, resolveOpenVikingActorPeerId, resolveOpenVikingMessagePeerId, sanitizeOpenVikingPeerId, } from "../routing/identity-routing.js";
|
|
6
6
|
import { extractNewTurnMessages } from "../text-utils.js";
|
|
7
7
|
import { estimateAgentMessageTokens, estimateTextTokens } from "../token-estimator.js";
|
|
8
8
|
import { convertToAgentMessages, mergeConsecutiveAssistants, sanitizeAgentMessagesForProvider, toRoleId, } from "./context-message-adapter.js";
|
|
@@ -26,12 +26,12 @@ function sleep(ms) {
|
|
|
26
26
|
* After wait=false commit, Phase2 runs on the server. Poll task until completed/failed/timeout
|
|
27
27
|
* so logs show memories_extracted (otherwise it looks like "nothing was saved").
|
|
28
28
|
*/
|
|
29
|
-
async function pollPhase2ExtractionOutcome(client, taskId,
|
|
29
|
+
async function pollPhase2ExtractionOutcome(client, taskId, logger, sessionLabel) {
|
|
30
30
|
const deadline = Date.now() + PHASE2_POLL_MAX_MS;
|
|
31
31
|
try {
|
|
32
32
|
while (Date.now() < deadline) {
|
|
33
33
|
await sleep(PHASE2_POLL_INTERVAL_MS);
|
|
34
|
-
const task = await client.getTask(taskId
|
|
34
|
+
const task = await client.getTask(taskId).catch((e) => {
|
|
35
35
|
logger.warn?.(`openviking: phase2 getTask failed task_id=${taskId}: ${String(e)}`);
|
|
36
36
|
return null;
|
|
37
37
|
});
|
|
@@ -141,7 +141,7 @@ function buildAssembledContext(overview, preAbstracts, ovMessages, tokenBudget,
|
|
|
141
141
|
const sanitized = sanitizeAgentMessagesForProvider(assembled);
|
|
142
142
|
return { sanitized, archive, session, budgets, instruction };
|
|
143
143
|
}
|
|
144
|
-
export async function commitOpenVikingSession({ sessionId, sessionKey, getClient, logger,
|
|
144
|
+
export async function commitOpenVikingSession({ sessionId, sessionKey, getClient, logger, rememberSessionAgentId, isBypassedSession, }) {
|
|
145
145
|
const ovId = openClawSessionToOvStorageId(sessionId, sessionKey);
|
|
146
146
|
if (isBypassedSession({ sessionId, sessionKey })) {
|
|
147
147
|
logger.warn?.(`openviking: commit skipped because session is bypassed (sessionId=${sessionId}, sessionKey=${sessionKey ?? "none"})`);
|
|
@@ -154,10 +154,8 @@ export async function commitOpenVikingSession({ sessionId, sessionKey, getClient
|
|
|
154
154
|
sessionKey,
|
|
155
155
|
ovSessionId: ovId,
|
|
156
156
|
});
|
|
157
|
-
const agentId = resolveAgentId(sessionId, sessionKey, ovId);
|
|
158
157
|
const commitResult = await client.commitSession(ovId, {
|
|
159
158
|
wait: true,
|
|
160
|
-
agentId,
|
|
161
159
|
keepRecentCount: 0,
|
|
162
160
|
});
|
|
163
161
|
const memCount = totalExtractedMemories(commitResult.memories_extracted);
|
|
@@ -191,6 +189,10 @@ function assemblePassthrough(params) {
|
|
|
191
189
|
});
|
|
192
190
|
return { messages: liveMessages, estimatedTokens: originalTokens };
|
|
193
191
|
}
|
|
192
|
+
function isSessionNotFoundError(err) {
|
|
193
|
+
const errorMessage = String(err);
|
|
194
|
+
return errorMessage.includes("[NOT_FOUND]") && errorMessage.includes("Session not found");
|
|
195
|
+
}
|
|
194
196
|
export async function assembleOpenVikingSession({ sessionId, sessionKey, messages, tokenBudget, runtimeContext, isMainAssemble, cfg, getClient, logger, resolveAgentId, rememberSessionAgentId, isBypassedSession, queryConfigStore, traceRecorder, diag, roughEstimate, messageDigest, extractAgentMessageText, hasAutoRecallBlock, prependRecallToLatestUserMessage, }) {
|
|
195
197
|
const ovSessionId = openClawSessionToOvStorageId(sessionId, sessionKey);
|
|
196
198
|
const sender = extractRuntimeSenderId(runtimeContext);
|
|
@@ -244,6 +246,11 @@ export async function assembleOpenVikingSession({ sessionId, sessionKey, message
|
|
|
244
246
|
const client = await getClient();
|
|
245
247
|
const routingRef = sessionId ?? sessionKey ?? ovSessionId;
|
|
246
248
|
const agentId = resolveAgentId(routingRef, sessionKey, ovSessionId);
|
|
249
|
+
const actorPeerId = resolveOpenVikingActorPeerId({
|
|
250
|
+
peerRole: cfg.peer_role ?? "assistant",
|
|
251
|
+
personPeerId: sanitizeOpenVikingPeerId(sender.senderId),
|
|
252
|
+
assistantPeerId: agentId,
|
|
253
|
+
});
|
|
247
254
|
const queryConfig = await queryConfigStore?.getEffective({
|
|
248
255
|
agentId,
|
|
249
256
|
sessionId,
|
|
@@ -255,6 +262,7 @@ export async function assembleOpenVikingSession({ sessionId, sessionKey, message
|
|
|
255
262
|
queryConfig,
|
|
256
263
|
client,
|
|
257
264
|
agentId,
|
|
265
|
+
actorPeerId,
|
|
258
266
|
queryText: recallQuery.query,
|
|
259
267
|
logger,
|
|
260
268
|
verbose: (message) => logger.info(message),
|
|
@@ -303,9 +311,7 @@ export async function assembleOpenVikingSession({ sessionId, sessionKey, message
|
|
|
303
311
|
}
|
|
304
312
|
try {
|
|
305
313
|
const client = await getClient();
|
|
306
|
-
const
|
|
307
|
-
const agentId = resolveAgentId(routingRef, sessionKey, ovSessionId);
|
|
308
|
-
const ctx = await client.getSessionContext(ovSessionId, tokenBudget, agentId);
|
|
314
|
+
const ctx = await client.getSessionContext(ovSessionId, tokenBudget);
|
|
309
315
|
const preAbstracts = ctx?.pre_archive_abstracts ?? [];
|
|
310
316
|
const hasArchives = !!ctx?.latest_archive_overview || preAbstracts.length > 0;
|
|
311
317
|
const activeCount = ctx?.messages?.length ?? 0;
|
|
@@ -368,6 +374,25 @@ export async function assembleOpenVikingSession({ sessionId, sessionKey, message
|
|
|
368
374
|
};
|
|
369
375
|
}
|
|
370
376
|
catch (err) {
|
|
377
|
+
if (isSessionNotFoundError(err)) {
|
|
378
|
+
const errorMessage = String(err);
|
|
379
|
+
logger.info(`openviking: assemble skipped because OV session does not exist ` +
|
|
380
|
+
`(session=${ovSessionId}, tokenBudget=${tokenBudget}, agentId=${resolveAgentId(ovSessionId)})`);
|
|
381
|
+
return assemblePassthrough({
|
|
382
|
+
diag,
|
|
383
|
+
ovSessionId,
|
|
384
|
+
reason: "session_not_found",
|
|
385
|
+
liveMessages: messages,
|
|
386
|
+
originalTokens,
|
|
387
|
+
extra: {
|
|
388
|
+
error: errorMessage,
|
|
389
|
+
tokenBudget,
|
|
390
|
+
agentId: resolveAgentId(ovSessionId),
|
|
391
|
+
senderIdFound: sender.found,
|
|
392
|
+
senderId: sender.senderId ?? null,
|
|
393
|
+
},
|
|
394
|
+
});
|
|
395
|
+
}
|
|
371
396
|
logger.warn?.(`openviking: assemble failed for session=${ovSessionId}, ` +
|
|
372
397
|
`tokenBudget=${tokenBudget}, agentId=${resolveAgentId(ovSessionId)}: ${String(err)}`);
|
|
373
398
|
diag("assemble_error", ovSessionId, {
|
|
@@ -421,7 +446,7 @@ function extractRuntimeAgentId(runtimeContext) {
|
|
|
421
446
|
return typeof agentId === "string" && agentId.trim() ? agentId.trim() : undefined;
|
|
422
447
|
}
|
|
423
448
|
function isToolOnlyMessage(msg) {
|
|
424
|
-
return msg.role === "
|
|
449
|
+
return msg.role === "assistant" && msg.parts.length > 0 && msg.parts.every((part) => part.type === "tool");
|
|
425
450
|
}
|
|
426
451
|
function coalesceConsecutiveToolMessages(messages) {
|
|
427
452
|
const result = [];
|
|
@@ -435,7 +460,7 @@ function coalesceConsecutiveToolMessages(messages) {
|
|
|
435
460
|
for (const msg of messages) {
|
|
436
461
|
if (isToolOnlyMessage(msg)) {
|
|
437
462
|
if (!pendingTools) {
|
|
438
|
-
pendingTools = { role: "
|
|
463
|
+
pendingTools = { role: "assistant", parts: [] };
|
|
439
464
|
}
|
|
440
465
|
pendingTools.parts.push(...msg.parts);
|
|
441
466
|
continue;
|
|
@@ -480,7 +505,7 @@ function messageDigest(messages, maxCharsPerMsg = 2000) {
|
|
|
480
505
|
};
|
|
481
506
|
});
|
|
482
507
|
}
|
|
483
|
-
export async function afterTurnOpenVikingSession({ sessionId, sessionKey, messages: rawMessages, prePromptMessageCount, isHeartbeat, runtimeContext, cfg, getClient, logger, resolveAgentId, rememberSessionAgentId, isBypassedSession, diag, }) {
|
|
508
|
+
export async function afterTurnOpenVikingSession({ sessionId, sessionKey, messages: rawMessages, prePromptMessageCount, isHeartbeat, tokenBudget, runtimeContext, cfg, getClient, logger, resolveAgentId, rememberSessionAgentId, isBypassedSession, diag, }) {
|
|
484
509
|
if (!cfg.autoCapture) {
|
|
485
510
|
return;
|
|
486
511
|
}
|
|
@@ -573,16 +598,24 @@ export async function afterTurnOpenVikingSession({ sessionId, sessionKey, messag
|
|
|
573
598
|
};
|
|
574
599
|
});
|
|
575
600
|
if (ovParts.length > 0) {
|
|
576
|
-
await client.addSessionMessage(ovSessionId, msg.role, ovParts,
|
|
601
|
+
await client.addSessionMessage(ovSessionId, msg.role, ovParts, undefined, createdAt, resolveOpenVikingMessagePeerId({
|
|
602
|
+
peerRole: cfg.peer_role ?? "assistant",
|
|
603
|
+
role: msg.role,
|
|
604
|
+
personPeerId: senderRoleId,
|
|
605
|
+
assistantPeerId: agentId,
|
|
606
|
+
}));
|
|
577
607
|
}
|
|
578
608
|
}
|
|
579
|
-
const session = await client.getSession(ovSessionId
|
|
609
|
+
const session = await client.getSession(ovSessionId);
|
|
580
610
|
const pendingTokens = session.pending_tokens ?? 0;
|
|
581
|
-
|
|
611
|
+
const commitTokenThreshold = Math.floor(tokenBudget * cfg.commitTokenThresholdRatio);
|
|
612
|
+
if (pendingTokens < commitTokenThreshold) {
|
|
582
613
|
diag("afterTurn_skip", ovSessionId, {
|
|
583
614
|
reason: "below_threshold",
|
|
584
615
|
pendingTokens,
|
|
585
|
-
commitTokenThreshold
|
|
616
|
+
commitTokenThreshold,
|
|
617
|
+
commitTokenThresholdRatio: cfg.commitTokenThresholdRatio,
|
|
618
|
+
tokenBudget,
|
|
586
619
|
senderIdFound: sender.found,
|
|
587
620
|
senderId: sender.senderId ?? null,
|
|
588
621
|
});
|
|
@@ -590,7 +623,6 @@ export async function afterTurnOpenVikingSession({ sessionId, sessionKey, messag
|
|
|
590
623
|
}
|
|
591
624
|
const commitResult = await client.commitSession(ovSessionId, {
|
|
592
625
|
wait: false,
|
|
593
|
-
agentId,
|
|
594
626
|
keepRecentCount: cfg.commitKeepRecentCount,
|
|
595
627
|
});
|
|
596
628
|
logger.info(`openviking: committed session=${ovSessionId}, ` +
|
|
@@ -598,7 +630,9 @@ export async function afterTurnOpenVikingSession({ sessionId, sessionKey, messag
|
|
|
598
630
|
`task_id=${commitResult.task_id ?? "none"}, trace_id=${commitResult.trace_id ?? "none"}`);
|
|
599
631
|
diag("afterTurn_commit", ovSessionId, {
|
|
600
632
|
pendingTokens,
|
|
601
|
-
commitTokenThreshold
|
|
633
|
+
commitTokenThreshold,
|
|
634
|
+
commitTokenThresholdRatio: cfg.commitTokenThresholdRatio,
|
|
635
|
+
tokenBudget,
|
|
602
636
|
status: commitResult.status,
|
|
603
637
|
archived: commitResult.archived ?? false,
|
|
604
638
|
taskId: commitResult.task_id ?? null,
|
|
@@ -609,7 +643,7 @@ export async function afterTurnOpenVikingSession({ sessionId, sessionKey, messag
|
|
|
609
643
|
if (commitResult.task_id && cfg.logFindRequests) {
|
|
610
644
|
logger.info(`openviking: Phase2 memory extraction runs asynchronously on the server (task_id=${commitResult.task_id}). ` +
|
|
611
645
|
"memories_extracted appears only after that task completes — not in this immediate response.");
|
|
612
|
-
void pollPhase2ExtractionOutcome(client, commitResult.task_id,
|
|
646
|
+
void pollPhase2ExtractionOutcome(client, commitResult.task_id, logger, ovSessionId);
|
|
613
647
|
}
|
|
614
648
|
}
|
|
615
649
|
catch (err) {
|
|
@@ -669,7 +703,7 @@ export async function compactOpenVikingSession({ sessionId, sessionKey, tokenBud
|
|
|
669
703
|
let preCommitEstimatedTokens;
|
|
670
704
|
if (typeof tokensBeforeOriginal !== "number") {
|
|
671
705
|
try {
|
|
672
|
-
const preCtx = await client.getSessionContext(ovSessionId, tokenBudget
|
|
706
|
+
const preCtx = await client.getSessionContext(ovSessionId, tokenBudget);
|
|
673
707
|
if (typeof preCtx.estimatedTokens === "number" && Number.isFinite(preCtx.estimatedTokens)) {
|
|
674
708
|
preCommitEstimatedTokens = preCtx.estimatedTokens;
|
|
675
709
|
}
|
|
@@ -684,7 +718,6 @@ export async function compactOpenVikingSession({ sessionId, sessionKey, tokenBud
|
|
|
684
718
|
logger.info(`openviking: compact committing session=${ovSessionId} (wait=true, tokenBudget=${tokenBudget})`);
|
|
685
719
|
const commitResult = await client.commitSession(ovSessionId, {
|
|
686
720
|
wait: true,
|
|
687
|
-
agentId,
|
|
688
721
|
keepRecentCount: 0,
|
|
689
722
|
});
|
|
690
723
|
const memCount = totalExtractedMemories(commitResult.memories_extracted);
|
|
@@ -746,7 +779,7 @@ export async function compactOpenVikingSession({ sessionId, sessionKey, tokenBud
|
|
|
746
779
|
let tokensAfter;
|
|
747
780
|
let contextFetchError;
|
|
748
781
|
try {
|
|
749
|
-
const ctx = await client.getSessionContext(ovSessionId, tokenBudget
|
|
782
|
+
const ctx = await client.getSessionContext(ovSessionId, tokenBudget);
|
|
750
783
|
logger.info(`openviking: compact getSessionContext raw result for ${ovSessionId}: ` +
|
|
751
784
|
JSON.stringify(ctx, null, 2));
|
|
752
785
|
if (typeof ctx.latest_archive_overview === "string") {
|
|
@@ -823,7 +856,7 @@ export async function compactOpenVikingSession({ sessionId, sessionKey, tokenBud
|
|
|
823
856
|
}
|
|
824
857
|
catch (err) {
|
|
825
858
|
const errorMessage = String(err);
|
|
826
|
-
if (
|
|
859
|
+
if (isSessionNotFoundError(err)) {
|
|
827
860
|
logger.info(`openviking: compact skipped because OV session does not exist ` +
|
|
828
861
|
`(session=${ovSessionId}, agentId=${agentId})`);
|
|
829
862
|
diag("compact_result", ovSessionId, {
|
|
@@ -1,14 +1,7 @@
|
|
|
1
|
+
import { sanitizeOpenVikingPeerId } from "../routing/identity-routing.js";
|
|
1
2
|
import { sanitizeToolUseResultPairing } from "../session-transcript-repair.js";
|
|
2
3
|
export function toRoleId(senderId) {
|
|
3
|
-
|
|
4
|
-
return undefined;
|
|
5
|
-
}
|
|
6
|
-
const normalized = senderId
|
|
7
|
-
.trim()
|
|
8
|
-
.replace(/[^a-zA-Z0-9_-]+/g, "_")
|
|
9
|
-
.replace(/^_+|_+$/g, "")
|
|
10
|
-
.replace(/_+/g, "_");
|
|
11
|
-
return normalized || undefined;
|
|
4
|
+
return sanitizeOpenVikingPeerId(senderId);
|
|
12
5
|
}
|
|
13
6
|
/**
|
|
14
7
|
* Convert an OpenViking stored message (parts-based format) into one or more
|
package/dist/text-utils.js
CHANGED
|
@@ -36,13 +36,8 @@ function looksLikeMetadataJsonBlock(content) {
|
|
|
36
36
|
}
|
|
37
37
|
return matchedKeys.size >= 3;
|
|
38
38
|
}
|
|
39
|
-
const HEARTBEAT_RE = /\bHEARTBEAT(?:\.md|_OK)\b/;
|
|
40
39
|
const TOOL_PLACEHOLDER_RE = /^\s*\[tool(?::\s*|Use:\s*)[^\]]+\]\s*$/i;
|
|
41
40
|
export function sanitizeUserTextForCapture(text) {
|
|
42
|
-
// 过滤 HEARTBEAT 健康检查消息
|
|
43
|
-
if (HEARTBEAT_RE.test(text)) {
|
|
44
|
-
return "";
|
|
45
|
-
}
|
|
46
41
|
// Drop legacy synthetic tool placeholders before they reach memory extraction.
|
|
47
42
|
if (TOOL_PLACEHOLDER_RE.test(text)) {
|
|
48
43
|
return "";
|
|
@@ -344,7 +339,7 @@ export function extractNewTurnMessages(messages, startIndex) {
|
|
|
344
339
|
: undefined);
|
|
345
340
|
if (output) {
|
|
346
341
|
result.push({
|
|
347
|
-
role: "
|
|
342
|
+
role: "assistant",
|
|
348
343
|
parts: [{
|
|
349
344
|
type: "tool",
|
|
350
345
|
toolCallId: toolCallId || undefined,
|
package/index.ts
CHANGED
|
@@ -41,7 +41,7 @@ import {
|
|
|
41
41
|
import type { CommandDefinition } from "./plugin/command-registration.js";
|
|
42
42
|
|
|
43
43
|
import type { HttpTransport } from "./adapters/http-transport.js";
|
|
44
|
-
import { formatMessageFaithful
|
|
44
|
+
import { formatMessageFaithful } from "./services/context-message-adapter.js";
|
|
45
45
|
import {
|
|
46
46
|
clampScore,
|
|
47
47
|
postProcessMemories,
|
|
@@ -212,6 +212,7 @@ const contextEnginePlugin = {
|
|
|
212
212
|
resolvePluginSessionRouting,
|
|
213
213
|
toQueryConfigContext,
|
|
214
214
|
} = createOpenVikingSessionRoutingRuntime({
|
|
215
|
+
peerRole: cfg.peer_role,
|
|
215
216
|
peerPrefix: cfg.peer_prefix,
|
|
216
217
|
logFindRequests: cfg.logFindRequests,
|
|
217
218
|
logger: api.logger,
|
|
@@ -329,8 +330,7 @@ const contextEnginePlugin = {
|
|
|
329
330
|
getClient,
|
|
330
331
|
normalizeSessionId: openClawSessionRefToOvStorageId,
|
|
331
332
|
createTempSessionId: createMemoryStoreTempSessionId,
|
|
332
|
-
|
|
333
|
-
toRoleId,
|
|
333
|
+
peerRole: cfg.peer_role,
|
|
334
334
|
resolvePluginSessionRouting,
|
|
335
335
|
isBypassedSession,
|
|
336
336
|
makeBypassedToolResult,
|
package/install-manifest.json
CHANGED
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
"tool-call-id.ts",
|
|
21
21
|
"session-transcript-repair.ts",
|
|
22
22
|
"runtime-utils.ts",
|
|
23
|
+
"request-headers.ts",
|
|
23
24
|
"recall-trace.ts",
|
|
24
25
|
"query-config.ts",
|
|
25
26
|
"adapters/",
|
|
@@ -52,7 +53,7 @@
|
|
|
52
53
|
"pruneAfterBuild": true
|
|
53
54
|
},
|
|
54
55
|
"compatibility": {
|
|
55
|
-
"minOpenclawVersion": "2026.
|
|
56
|
+
"minOpenclawVersion": "2026.5.27",
|
|
56
57
|
"recommendedOpenclawVersion": "2026.6.6",
|
|
57
58
|
"minOpenvikingVersion": "0.4.1",
|
|
58
59
|
"recommendedOpenvikingVersion": "0.4.1"
|
package/openclaw.plugin.json
CHANGED
|
@@ -98,6 +98,11 @@
|
|
|
98
98
|
"placeholder": "${OPENVIKING_API_KEY}",
|
|
99
99
|
"help": "Optional API key for OpenViking server"
|
|
100
100
|
},
|
|
101
|
+
"headers": {
|
|
102
|
+
"label": "Headers",
|
|
103
|
+
"advanced": true,
|
|
104
|
+
"help": "Optional HTTP headers merged into every OpenViking request."
|
|
105
|
+
},
|
|
101
106
|
"accountId": {
|
|
102
107
|
"label": "Account ID",
|
|
103
108
|
"placeholder": "(derived from API key)",
|
|
@@ -196,11 +201,11 @@
|
|
|
196
201
|
"help": "Completely bypass OpenViking for matching session keys. Use * within one segment and ** across segments.",
|
|
197
202
|
"advanced": true
|
|
198
203
|
},
|
|
199
|
-
"
|
|
200
|
-
"label": "Commit Token Threshold",
|
|
201
|
-
"placeholder": "
|
|
204
|
+
"commitTokenThresholdRatio": {
|
|
205
|
+
"label": "Commit Token Threshold Ratio",
|
|
206
|
+
"placeholder": "0.5",
|
|
202
207
|
"advanced": true,
|
|
203
|
-
"help": "
|
|
208
|
+
"help": "Auto-commit triggers once estimated pending tokens reach this fraction (0-1) of the model context window (e.g. 0.5 = 50%). Set to 0 to commit every turn."
|
|
204
209
|
},
|
|
205
210
|
"commitKeepRecentCount": {
|
|
206
211
|
"label": "Commit Keep Recent Count",
|
|
@@ -287,6 +292,12 @@
|
|
|
287
292
|
"apiKey": {
|
|
288
293
|
"type": "string"
|
|
289
294
|
},
|
|
295
|
+
"headers": {
|
|
296
|
+
"type": "object",
|
|
297
|
+
"additionalProperties": {
|
|
298
|
+
"type": "string"
|
|
299
|
+
}
|
|
300
|
+
},
|
|
290
301
|
"accountId": {
|
|
291
302
|
"type": "string"
|
|
292
303
|
},
|
|
@@ -358,6 +369,11 @@
|
|
|
358
369
|
"commitTokenThreshold": {
|
|
359
370
|
"type": "number"
|
|
360
371
|
},
|
|
372
|
+
"commitTokenThresholdRatio": {
|
|
373
|
+
"type": "number",
|
|
374
|
+
"minimum": 0,
|
|
375
|
+
"maximum": 1
|
|
376
|
+
},
|
|
361
377
|
"commitKeepRecentCount": {
|
|
362
378
|
"type": "number",
|
|
363
379
|
"minimum": 0,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openviking/openclaw-plugin",
|
|
3
|
-
"version": "2026.
|
|
3
|
+
"version": "2026.7.15",
|
|
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",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"test:watch": "vitest"
|
|
48
48
|
},
|
|
49
49
|
"engines": {
|
|
50
|
-
"openclaw": ">=2026.
|
|
50
|
+
"openclaw": ">=2026.5.27"
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
53
|
"@sinclair/typebox": "0.34.48",
|
|
@@ -69,12 +69,12 @@
|
|
|
69
69
|
],
|
|
70
70
|
"setupEntry": "./dist/commands/setup.js",
|
|
71
71
|
"compat": {
|
|
72
|
-
"pluginApi": ">=2026.
|
|
73
|
-
"minGatewayVersion": "2026.
|
|
72
|
+
"pluginApi": ">=2026.5.27",
|
|
73
|
+
"minGatewayVersion": "2026.5.27"
|
|
74
74
|
},
|
|
75
75
|
"build": {
|
|
76
|
-
"openclawVersion": "2026.
|
|
77
|
-
"pluginSdkVersion": "2026.
|
|
76
|
+
"openclawVersion": "2026.5.27",
|
|
77
|
+
"pluginSdkVersion": "2026.5.27"
|
|
78
78
|
}
|
|
79
79
|
},
|
|
80
80
|
"devDependencies": {
|
|
@@ -26,7 +26,7 @@ export type OpenVikingArchiveClient = {
|
|
|
26
26
|
grepSessionArchives: (
|
|
27
27
|
sessionId: string,
|
|
28
28
|
pattern: string,
|
|
29
|
-
options: { archiveId?: string; caseInsensitive: boolean
|
|
29
|
+
options: { archiveId?: string; caseInsensitive: boolean },
|
|
30
30
|
) => Promise<{
|
|
31
31
|
count?: number;
|
|
32
32
|
matches?: OpenVikingArchiveMatch[];
|
|
@@ -34,7 +34,6 @@ export type OpenVikingArchiveClient = {
|
|
|
34
34
|
getSessionArchive: (
|
|
35
35
|
sessionId: string,
|
|
36
36
|
archiveId: string,
|
|
37
|
-
agentId?: string,
|
|
38
37
|
) => Promise<{
|
|
39
38
|
archive_id: string;
|
|
40
39
|
abstract?: string;
|
|
@@ -128,7 +127,6 @@ export function registerOpenVikingArchiveTools(deps: OpenVikingArchiveToolsDeps)
|
|
|
128
127
|
const result = await client.grepSessionArchives(ovSessionId, escapedQuery, {
|
|
129
128
|
archiveId,
|
|
130
129
|
caseInsensitive: true,
|
|
131
|
-
agentId,
|
|
132
130
|
});
|
|
133
131
|
const traceResults: RecallTraceResult[] = (result.matches ?? []).slice(0, deps.traceRecallMaxResultsPerSearch).map((match) => ({
|
|
134
132
|
uri: match.uri,
|
|
@@ -263,7 +261,6 @@ export function registerOpenVikingArchiveTools(deps: OpenVikingArchiveToolsDeps)
|
|
|
263
261
|
const detail = await client.getSessionArchive(
|
|
264
262
|
session.ovSessionId,
|
|
265
263
|
archiveId,
|
|
266
|
-
session.agentId,
|
|
267
264
|
);
|
|
268
265
|
|
|
269
266
|
const header = [
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { OpenVikingClient } from "../client.js";
|
|
2
2
|
import type { HttpTransport } from "../adapters/http-transport.js";
|
|
3
|
+
import { resolveOpenVikingRequestHeaders } from "../request-headers.js";
|
|
3
4
|
|
|
4
5
|
type Logger = {
|
|
5
6
|
info: (message: string) => void;
|
|
@@ -13,6 +14,7 @@ type ClientRuntimeConfig = {
|
|
|
13
14
|
timeoutMs: number;
|
|
14
15
|
accountId?: string;
|
|
15
16
|
userId?: string;
|
|
17
|
+
headers?: Record<string, string>;
|
|
16
18
|
logFindRequests: boolean;
|
|
17
19
|
};
|
|
18
20
|
|
|
@@ -61,7 +63,12 @@ export function createOpenVikingClientRuntime(options: {
|
|
|
61
63
|
cfg.accountId,
|
|
62
64
|
cfg.userId,
|
|
63
65
|
routingDebugLog,
|
|
64
|
-
{
|
|
66
|
+
{
|
|
67
|
+
transport: options.transport,
|
|
68
|
+
headers: resolveOpenVikingRequestHeaders({
|
|
69
|
+
headers: cfg.headers,
|
|
70
|
+
}),
|
|
71
|
+
},
|
|
65
72
|
),
|
|
66
73
|
);
|
|
67
74
|
|
|
@@ -6,6 +6,7 @@ export type OpenVikingCommandSession = {
|
|
|
6
6
|
sessionKey?: string;
|
|
7
7
|
ovSessionId?: string;
|
|
8
8
|
agentId: string;
|
|
9
|
+
actorPeerId?: string;
|
|
9
10
|
};
|
|
10
11
|
|
|
11
12
|
export type OpenVikingCommandToolResult = {
|
|
@@ -217,7 +218,7 @@ export function createOpenVikingCommandDefinitions(
|
|
|
217
218
|
}
|
|
218
219
|
const session = deps.resolvePluginSessionRouting(ctx);
|
|
219
220
|
const input = deps.parseAddResourceCommandArgs(ctx.args ?? "");
|
|
220
|
-
return toCommandResult(await deps.addResourceOpenViking(input, session.
|
|
221
|
+
return toCommandResult(await deps.addResourceOpenViking(input, session.actorPeerId));
|
|
221
222
|
} catch (err) {
|
|
222
223
|
return { text: `OpenViking add resource failed: ${err instanceof Error ? err.message : String(err)}` };
|
|
223
224
|
}
|
|
@@ -234,7 +235,7 @@ export function createOpenVikingCommandDefinitions(
|
|
|
234
235
|
}
|
|
235
236
|
const session = deps.resolvePluginSessionRouting(ctx);
|
|
236
237
|
const input = deps.parseAddSkillCommandArgs(ctx.args ?? "");
|
|
237
|
-
return toCommandResult(await deps.addSkillOpenViking(input, session.
|
|
238
|
+
return toCommandResult(await deps.addSkillOpenViking(input, session.actorPeerId));
|
|
238
239
|
} catch (err) {
|
|
239
240
|
return { text: `OpenViking add skill failed: ${err instanceof Error ? err.message : String(err)}` };
|
|
240
241
|
}
|
|
@@ -251,7 +252,7 @@ export function createOpenVikingCommandDefinitions(
|
|
|
251
252
|
}
|
|
252
253
|
const session = deps.resolvePluginSessionRouting(ctx);
|
|
253
254
|
const input = deps.parseOVSearchCommandArgs(ctx.args ?? "");
|
|
254
|
-
return toCommandResult(await deps.searchOpenViking(input, session.
|
|
255
|
+
return toCommandResult(await deps.searchOpenViking(input, session.actorPeerId, session));
|
|
255
256
|
} catch (err) {
|
|
256
257
|
return { text: `OpenViking search failed: ${err instanceof Error ? err.message : String(err)}` };
|
|
257
258
|
}
|
|
@@ -12,6 +12,7 @@ export type OpenVikingImportToolContext = {
|
|
|
12
12
|
sessionId?: string;
|
|
13
13
|
agentId?: string;
|
|
14
14
|
senderId?: string;
|
|
15
|
+
requesterSenderId?: string;
|
|
15
16
|
};
|
|
16
17
|
|
|
17
18
|
export type OpenVikingImportSession = {
|
|
@@ -19,6 +20,7 @@ export type OpenVikingImportSession = {
|
|
|
19
20
|
sessionKey?: string;
|
|
20
21
|
ovSessionId?: string;
|
|
21
22
|
agentId: string;
|
|
23
|
+
actorPeerId?: string;
|
|
22
24
|
};
|
|
23
25
|
|
|
24
26
|
export type OpenVikingImportClient = {
|
|
@@ -80,7 +82,7 @@ export function registerOpenVikingImportTools(deps: OpenVikingImportToolsDeps):
|
|
|
80
82
|
instruction: typeof params.instruction === "string" ? params.instruction : undefined,
|
|
81
83
|
wait: typeof params.wait === "boolean" ? params.wait : undefined,
|
|
82
84
|
timeout: typeof params.timeout === "number" ? params.timeout : undefined,
|
|
83
|
-
}, session.
|
|
85
|
+
}, session.actorPeerId);
|
|
84
86
|
return {
|
|
85
87
|
content: [{ type: "text" as const, text: formatResourceImportText(result) }],
|
|
86
88
|
details: {
|
|
@@ -118,7 +120,7 @@ export function registerOpenVikingImportTools(deps: OpenVikingImportToolsDeps):
|
|
|
118
120
|
data: params.data,
|
|
119
121
|
wait: typeof params.wait === "boolean" ? params.wait : undefined,
|
|
120
122
|
timeout: typeof params.timeout === "number" ? params.timeout : undefined,
|
|
121
|
-
}, session.
|
|
123
|
+
}, session.actorPeerId);
|
|
122
124
|
return {
|
|
123
125
|
content: [{ type: "text" as const, text: formatSkillImportText(result) }],
|
|
124
126
|
details: {
|
|
@@ -15,6 +15,7 @@ export type OpenVikingMemoryRecallToolContext = {
|
|
|
15
15
|
sessionId?: string;
|
|
16
16
|
agentId?: string;
|
|
17
17
|
senderId?: string;
|
|
18
|
+
requesterSenderId?: string;
|
|
18
19
|
};
|
|
19
20
|
|
|
20
21
|
export type OpenVikingMemoryRecallSession = {
|
|
@@ -22,6 +23,7 @@ export type OpenVikingMemoryRecallSession = {
|
|
|
22
23
|
sessionKey?: string;
|
|
23
24
|
ovSessionId?: string;
|
|
24
25
|
agentId: string;
|
|
26
|
+
actorPeerId?: string;
|
|
25
27
|
};
|
|
26
28
|
|
|
27
29
|
export type OpenVikingMemoryRecallClient = {
|
|
@@ -163,7 +165,7 @@ export function registerOpenVikingMemoryRecallTools(
|
|
|
163
165
|
const recallClient = await deps.getClient();
|
|
164
166
|
if (deps.cfg.logFindRequests) {
|
|
165
167
|
deps.logger.info?.(
|
|
166
|
-
`openviking: memory_recall X-OpenViking-Actor-Peer="${session.
|
|
168
|
+
`openviking: memory_recall X-OpenViking-Actor-Peer="${session.actorPeerId ?? "none"}" ` +
|
|
167
169
|
`(plugin defaultAgentId="${recallClient.getDefaultAgentId()}" is unused when session context is present)`,
|
|
168
170
|
);
|
|
169
171
|
}
|
|
@@ -177,7 +179,7 @@ export function registerOpenVikingMemoryRecallTools(
|
|
|
177
179
|
targetUri,
|
|
178
180
|
limit: requestLimit,
|
|
179
181
|
scoreThreshold: 0,
|
|
180
|
-
actorPeerId: session.
|
|
182
|
+
actorPeerId: session.actorPeerId,
|
|
181
183
|
},
|
|
182
184
|
);
|
|
183
185
|
const traceResults = [
|
|
@@ -207,7 +209,7 @@ export function registerOpenVikingMemoryRecallTools(
|
|
|
207
209
|
limit: requestLimit,
|
|
208
210
|
scoreThreshold: 0,
|
|
209
211
|
contextType: search.contextType,
|
|
210
|
-
actorPeerId: session.
|
|
212
|
+
actorPeerId: session.actorPeerId,
|
|
211
213
|
},
|
|
212
214
|
),
|
|
213
215
|
));
|
|
@@ -319,7 +321,7 @@ export function registerOpenVikingMemoryRecallTools(
|
|
|
319
321
|
}
|
|
320
322
|
const { lines: memoryLines } = await deps.buildMemoryLinesWithBudget(
|
|
321
323
|
memories,
|
|
322
|
-
(uri) => recallClient.read(uri, session.
|
|
324
|
+
(uri) => recallClient.read(uri, session.actorPeerId),
|
|
323
325
|
{
|
|
324
326
|
recallPreferAbstract: false,
|
|
325
327
|
recallMaxInjectedChars: queryConfig.maxInjectedChars,
|