@openviking/openclaw-plugin 2026.7.11 → 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.
Files changed (37) hide show
  1. package/auto-recall.ts +5 -4
  2. package/context-engine.ts +0 -1
  3. package/dist/auto-recall.js +4 -4
  4. package/dist/context-engine.js +0 -1
  5. package/dist/index.js +4 -4
  6. package/dist/plugin/openviking-archive-tools.js +1 -2
  7. package/dist/plugin/openviking-command-definitions.js +3 -3
  8. package/dist/plugin/openviking-import-tools.js +2 -2
  9. package/dist/plugin/openviking-memory-recall-tools.js +4 -4
  10. package/dist/plugin/openviking-memory-tools.js +11 -6
  11. package/dist/plugin/openviking-query-runtime.js +1 -1
  12. package/dist/plugin/openviking-query-tools.js +4 -4
  13. package/dist/plugin/openviking-recall-trace-runtime.js +3 -3
  14. package/dist/plugin/openviking-session-routing-runtime.js +9 -2
  15. package/dist/plugin/openviking-tool-result-tools.js +3 -3
  16. package/dist/routing/identity-routing.js +27 -0
  17. package/dist/routing/memory-uri.js +1 -0
  18. package/dist/services/context-lifecycle-service.js +21 -16
  19. package/dist/services/context-message-adapter.js +2 -9
  20. package/index.ts +3 -3
  21. package/package.json +1 -1
  22. package/plugin/command-registration.ts +2 -0
  23. package/plugin/openviking-archive-tools.ts +1 -4
  24. package/plugin/openviking-command-definitions.ts +4 -3
  25. package/plugin/openviking-import-tools.ts +4 -2
  26. package/plugin/openviking-memory-recall-tools.ts +6 -4
  27. package/plugin/openviking-memory-tools.ts +18 -10
  28. package/plugin/openviking-query-runtime.ts +1 -1
  29. package/plugin/openviking-query-tools.ts +6 -4
  30. package/plugin/openviking-recall-trace-runtime.ts +4 -3
  31. package/plugin/openviking-recall-trace-tools.ts +2 -0
  32. package/plugin/openviking-session-routing-runtime.ts +15 -1
  33. package/plugin/openviking-tool-result-tools.ts +0 -6
  34. package/routing/identity-routing.ts +41 -0
  35. package/routing/memory-uri.ts +1 -0
  36. package/services/context-lifecycle-service.ts +27 -18
  37. package/services/context-message-adapter.ts +2 -9
package/auto-recall.ts CHANGED
@@ -313,6 +313,7 @@ export async function buildAutoRecallContext(params: {
313
313
  queryConfig?: EffectiveQueryConfig;
314
314
  client: OpenVikingClient;
315
315
  agentId: string;
316
+ actorPeerId?: string;
316
317
  queryText: string;
317
318
  logger: Logger;
318
319
  verbose?: (message: string) => void;
@@ -324,14 +325,14 @@ export async function buildAutoRecallContext(params: {
324
325
  queryTruncated?: boolean;
325
326
  resourceTypes?: RecallResourceType[];
326
327
  }): Promise<{ block?: string; memoryCount: number; estimatedTokens: number }> {
327
- const { cfg, client, agentId, queryText, logger, verbose } = params;
328
+ const { cfg, client, agentId, actorPeerId, queryText, logger, verbose } = params;
328
329
  const queryConfig = params.queryConfig;
329
330
 
330
331
  if (!cfg.autoRecall || queryText.length < 5) {
331
332
  return { memoryCount: 0, estimatedTokens: 0 };
332
333
  }
333
334
 
334
- const precheck = await quickRecallPrecheck(client, agentId);
335
+ const precheck = await quickRecallPrecheck(client, actorPeerId);
335
336
  if (!precheck.ok) {
336
337
  verbose?.(`openviking: skipping auto-recall because precheck failed (${precheck.reason})`);
337
338
  return { memoryCount: 0, estimatedTokens: 0 };
@@ -369,7 +370,7 @@ export async function buildAutoRecallContext(params: {
369
370
  limit: candidateLimit,
370
371
  scoreThreshold: 0,
371
372
  contextType: search.contextType,
372
- actorPeerId: agentId,
373
+ actorPeerId,
373
374
  });
374
375
  return {
375
376
  resourceType: search.resourceType,
@@ -484,7 +485,7 @@ export async function buildAutoRecallContext(params: {
484
485
 
485
486
  const { lines: memoryLines, estimatedTokens } = await buildMemoryLinesWithBudget(
486
487
  memories,
487
- (uri) => client.read(uri, agentId),
488
+ (uri) => client.read(uri, actorPeerId),
488
489
  {
489
490
  recallPreferAbstract,
490
491
  recallMaxInjectedChars: maxInjectedChars,
package/context-engine.ts CHANGED
@@ -283,7 +283,6 @@ export function createMemoryOpenVikingContextEngine(params: {
283
283
  getClient,
284
284
  logger,
285
285
  rememberSessionAgentId,
286
- resolveAgentId,
287
286
  isBypassedSession,
288
287
  });
289
288
  }
@@ -200,12 +200,12 @@ export function shouldRecallAgentExperience(input) {
200
200
  return { recall: false, score, reason: score < 0 ? "non_execution" : "below_threshold" };
201
201
  }
202
202
  export async function buildAutoRecallContext(params) {
203
- const { cfg, client, agentId, queryText, logger, verbose } = params;
203
+ const { cfg, client, agentId, actorPeerId, queryText, logger, verbose } = params;
204
204
  const queryConfig = params.queryConfig;
205
205
  if (!cfg.autoRecall || queryText.length < 5) {
206
206
  return { memoryCount: 0, estimatedTokens: 0 };
207
207
  }
208
- const precheck = await quickRecallPrecheck(client, agentId);
208
+ const precheck = await quickRecallPrecheck(client, actorPeerId);
209
209
  if (!precheck.ok) {
210
210
  verbose?.(`openviking: skipping auto-recall because precheck failed (${precheck.reason})`);
211
211
  return { memoryCount: 0, estimatedTokens: 0 };
@@ -236,7 +236,7 @@ export async function buildAutoRecallContext(params) {
236
236
  limit: candidateLimit,
237
237
  scoreThreshold: 0,
238
238
  contextType: search.contextType,
239
- actorPeerId: agentId,
239
+ actorPeerId,
240
240
  });
241
241
  return {
242
242
  resourceType: search.resourceType,
@@ -343,7 +343,7 @@ export async function buildAutoRecallContext(params) {
343
343
  await recordTrace([], 0, 0);
344
344
  return { memoryCount: 0, estimatedTokens: 0 };
345
345
  }
346
- const { lines: memoryLines, estimatedTokens } = await buildMemoryLinesWithBudget(memories, (uri) => client.read(uri, agentId), {
346
+ const { lines: memoryLines, estimatedTokens } = await buildMemoryLinesWithBudget(memories, (uri) => client.read(uri, actorPeerId), {
347
347
  recallPreferAbstract,
348
348
  recallMaxInjectedChars: maxInjectedChars,
349
349
  includeUri: true,
@@ -135,7 +135,6 @@ export function createMemoryOpenVikingContextEngine(params) {
135
135
  getClient,
136
136
  logger,
137
137
  rememberSessionAgentId,
138
- resolveAgentId,
139
138
  isBypassedSession,
140
139
  });
141
140
  }
package/dist/index.js CHANGED
@@ -25,8 +25,8 @@ import { registerOpenVikingRecallTraceTools } from "./plugin/openviking-recall-t
25
25
  import { createOpenVikingRuntimeState } from "./plugin/openviking-runtime-state.js";
26
26
  import { createOpenVikingSessionRoutingRuntime } from "./plugin/openviking-session-routing-runtime.js";
27
27
  import { registerOpenVikingToolResultTools } from "./plugin/openviking-tool-result-tools.js";
28
- import { boundTraceQuery, createMemoryStoreTempSessionId, createTraceId, extractToolSenderId, inferRecallResourceType, makeBypassedToolResult, previewText, } from "./plugin/openviking-runtime-utils.js";
29
- import { formatMessageFaithful, toRoleId } from "./services/context-message-adapter.js";
28
+ import { boundTraceQuery, createMemoryStoreTempSessionId, createTraceId, inferRecallResourceType, makeBypassedToolResult, previewText, } from "./plugin/openviking-runtime-utils.js";
29
+ import { formatMessageFaithful } from "./services/context-message-adapter.js";
30
30
  import { clampScore, postProcessMemories, pickMemoriesForInjection, } from "./memory-ranking.js";
31
31
  import { createMemoryOpenVikingContextEngine, } from "./context-engine.js";
32
32
  import { openClawSessionRefToOvStorageId, openClawSessionToOvStorageId, } from "./routing/identity-routing.js";
@@ -80,6 +80,7 @@ const contextEnginePlugin = {
80
80
  logger: api.logger,
81
81
  });
82
82
  const { rememberSessionAgentId, resolveAgentId, resolvePluginSessionRouting, toQueryConfigContext, } = createOpenVikingSessionRoutingRuntime({
83
+ peerRole: cfg.peer_role,
83
84
  peerPrefix: cfg.peer_prefix,
84
85
  logFindRequests: cfg.logFindRequests,
85
86
  logger: api.logger,
@@ -182,8 +183,7 @@ const contextEnginePlugin = {
182
183
  getClient,
183
184
  normalizeSessionId: openClawSessionRefToOvStorageId,
184
185
  createTempSessionId: createMemoryStoreTempSessionId,
185
- extractSenderId: extractToolSenderId,
186
- toRoleId,
186
+ peerRole: cfg.peer_role,
187
187
  resolvePluginSessionRouting,
188
188
  isBypassedSession,
189
189
  makeBypassedToolResult,
@@ -55,7 +55,6 @@ export function registerOpenVikingArchiveTools(deps) {
55
55
  const result = await client.grepSessionArchives(ovSessionId, escapedQuery, {
56
56
  archiveId,
57
57
  caseInsensitive: true,
58
- agentId,
59
58
  });
60
59
  const traceResults = (result.matches ?? []).slice(0, deps.traceRecallMaxResultsPerSearch).map((match) => ({
61
60
  uri: match.uri,
@@ -175,7 +174,7 @@ export function registerOpenVikingArchiveTools(deps) {
175
174
  }
176
175
  try {
177
176
  const client = await deps.getClient();
178
- const detail = await client.getSessionArchive(session.ovSessionId, archiveId, session.agentId);
177
+ const detail = await client.getSessionArchive(session.ovSessionId, archiveId);
179
178
  const header = [
180
179
  `## ${detail.archive_id}`,
181
180
  detail.abstract ? `**Summary**: ${detail.abstract}` : "",
@@ -124,7 +124,7 @@ export function createOpenVikingCommandDefinitions(deps) {
124
124
  }
125
125
  const session = deps.resolvePluginSessionRouting(ctx);
126
126
  const input = deps.parseAddResourceCommandArgs(ctx.args ?? "");
127
- return toCommandResult(await deps.addResourceOpenViking(input, session.agentId));
127
+ return toCommandResult(await deps.addResourceOpenViking(input, session.actorPeerId));
128
128
  }
129
129
  catch (err) {
130
130
  return { text: `OpenViking add resource failed: ${err instanceof Error ? err.message : String(err)}` };
@@ -142,7 +142,7 @@ export function createOpenVikingCommandDefinitions(deps) {
142
142
  }
143
143
  const session = deps.resolvePluginSessionRouting(ctx);
144
144
  const input = deps.parseAddSkillCommandArgs(ctx.args ?? "");
145
- return toCommandResult(await deps.addSkillOpenViking(input, session.agentId));
145
+ return toCommandResult(await deps.addSkillOpenViking(input, session.actorPeerId));
146
146
  }
147
147
  catch (err) {
148
148
  return { text: `OpenViking add skill failed: ${err instanceof Error ? err.message : String(err)}` };
@@ -160,7 +160,7 @@ export function createOpenVikingCommandDefinitions(deps) {
160
160
  }
161
161
  const session = deps.resolvePluginSessionRouting(ctx);
162
162
  const input = deps.parseOVSearchCommandArgs(ctx.args ?? "");
163
- return toCommandResult(await deps.searchOpenViking(input, session.agentId, session));
163
+ return toCommandResult(await deps.searchOpenViking(input, session.actorPeerId, session));
164
164
  }
165
165
  catch (err) {
166
166
  return { text: `OpenViking search failed: ${err instanceof Error ? err.message : String(err)}` };
@@ -40,7 +40,7 @@ export function registerOpenVikingImportTools(deps) {
40
40
  instruction: typeof params.instruction === "string" ? params.instruction : undefined,
41
41
  wait: typeof params.wait === "boolean" ? params.wait : undefined,
42
42
  timeout: typeof params.timeout === "number" ? params.timeout : undefined,
43
- }, session.agentId);
43
+ }, session.actorPeerId);
44
44
  return {
45
45
  content: [{ type: "text", text: formatResourceImportText(result) }],
46
46
  details: {
@@ -73,7 +73,7 @@ export function registerOpenVikingImportTools(deps) {
73
73
  data: params.data,
74
74
  wait: typeof params.wait === "boolean" ? params.wait : undefined,
75
75
  timeout: typeof params.timeout === "number" ? params.timeout : undefined,
76
- }, session.agentId);
76
+ }, session.actorPeerId);
77
77
  return {
78
78
  content: [{ type: "text", text: formatSkillImportText(result) }],
79
79
  details: {
@@ -47,7 +47,7 @@ export function registerOpenVikingMemoryRecallTools(deps) {
47
47
  const requestLimit = queryConfig.candidateLimit;
48
48
  const recallClient = await deps.getClient();
49
49
  if (deps.cfg.logFindRequests) {
50
- deps.logger.info?.(`openviking: memory_recall X-OpenViking-Actor-Peer="${session.agentId}" ` +
50
+ deps.logger.info?.(`openviking: memory_recall X-OpenViking-Actor-Peer="${session.actorPeerId ?? "none"}" ` +
51
51
  `(plugin defaultAgentId="${recallClient.getDefaultAgentId()}" is unused when session context is present)`);
52
52
  }
53
53
  let result;
@@ -57,7 +57,7 @@ export function registerOpenVikingMemoryRecallTools(deps) {
57
57
  targetUri,
58
58
  limit: requestLimit,
59
59
  scoreThreshold: 0,
60
- actorPeerId: session.agentId,
60
+ actorPeerId: session.actorPeerId,
61
61
  });
62
62
  const traceResults = [
63
63
  ...(result.memories ?? []).map((item) => toTraceResult(item, "memory", deps)),
@@ -84,7 +84,7 @@ export function registerOpenVikingMemoryRecallTools(deps) {
84
84
  limit: requestLimit,
85
85
  scoreThreshold: 0,
86
86
  contextType: search.contextType,
87
- actorPeerId: session.agentId,
87
+ actorPeerId: session.actorPeerId,
88
88
  })));
89
89
  const allMemories = [];
90
90
  for (let index = 0; index < settled.length; index += 1) {
@@ -188,7 +188,7 @@ export function registerOpenVikingMemoryRecallTools(deps) {
188
188
  details: { count: 0, total: result.total ?? 0, scoreThreshold },
189
189
  };
190
190
  }
191
- const { lines: memoryLines } = await deps.buildMemoryLinesWithBudget(memories, (uri) => recallClient.read(uri, session.agentId), {
191
+ const { lines: memoryLines } = await deps.buildMemoryLinesWithBudget(memories, (uri) => recallClient.read(uri, session.actorPeerId), {
192
192
  recallPreferAbstract: false,
193
193
  recallMaxInjectedChars: queryConfig.maxInjectedChars,
194
194
  });
@@ -1,5 +1,6 @@
1
1
  import { Type } from "@sinclair/typebox";
2
2
  import { clampScore, postProcessMemories } from "../memory-ranking.js";
3
+ import { resolveOpenVikingMessagePeerId, } from "../routing/identity-routing.js";
3
4
  import { isMemoryUri } from "../routing/memory-uri.js";
4
5
  function totalCommitMemories(r) {
5
6
  const m = r.memories_extracted;
@@ -41,11 +42,15 @@ export function registerOpenVikingMemoryTools(deps) {
41
42
  sessionId = deps.createTempSessionId();
42
43
  usedTempSession = true;
43
44
  }
44
- const roleId = role === "user" ? deps.toRoleId(deps.extractSenderId(ctx)) : undefined;
45
- await client.addSessionMessage(sessionId, role, [{ type: "text", text }], session.agentId, undefined, roleId);
45
+ const peerId = resolveOpenVikingMessagePeerId({
46
+ peerRole: deps.peerRole,
47
+ role,
48
+ personPeerId: session.actorPeerId,
49
+ assistantPeerId: session.actorPeerId,
50
+ });
51
+ await client.addSessionMessage(sessionId, role, [{ type: "text", text }], undefined, undefined, peerId);
46
52
  const commitResult = await client.commitSession(sessionId, {
47
53
  wait: true,
48
- agentId: session.agentId,
49
54
  keepRecentCount: 0,
50
55
  });
51
56
  const memoriesCount = totalCommitMemories(commitResult);
@@ -130,7 +135,7 @@ export function registerOpenVikingMemoryTools(deps) {
130
135
  details: { action: "rejected", uri },
131
136
  };
132
137
  }
133
- await client.deleteUri(uri, session.agentId);
138
+ await client.deleteUri(uri, session.actorPeerId);
134
139
  return {
135
140
  content: [{ type: "text", text: `Forgotten: ${uri}` }],
136
141
  details: { action: "deleted", uri },
@@ -157,7 +162,7 @@ export function registerOpenVikingMemoryTools(deps) {
157
162
  targetUri,
158
163
  limit: requestLimit,
159
164
  scoreThreshold: 0,
160
- }, session.agentId);
165
+ }, session.actorPeerId);
161
166
  const candidates = postProcessMemories(result.memories ?? [], {
162
167
  limit: requestLimit,
163
168
  scoreThreshold,
@@ -176,7 +181,7 @@ export function registerOpenVikingMemoryTools(deps) {
176
181
  }
177
182
  const top = candidates[0];
178
183
  if (candidates.length === 1 && clampScore(top.score) >= 0.85) {
179
- await client.deleteUri(top.uri, session.agentId);
184
+ await client.deleteUri(top.uri, session.actorPeerId);
180
185
  return {
181
186
  content: [{ type: "text", text: `Forgotten: ${top.uri}` }],
182
187
  details: { action: "deleted", uri: top.uri, score: top.score ?? 0 },
@@ -337,7 +337,7 @@ export function createOpenVikingQueryRuntime(deps) {
337
337
  sessionId: traceCtx?.sessionId,
338
338
  sessionKey: traceCtx?.sessionKey,
339
339
  ovSessionId: traceCtx?.ovSessionId,
340
- agentId,
340
+ agentId: traceCtx?.agentId,
341
341
  source: "ov_search",
342
342
  operationType: "semantic_find",
343
343
  resourceTypes: [...new Set(searches.map((search) => search.resourceType).filter((resourceType) => resourceType !== "archive"))],
@@ -21,7 +21,7 @@ export function registerOpenVikingQueryTools(deps) {
21
21
  query: String(params.query ?? ""),
22
22
  uri: typeof params.uri === "string" ? params.uri : undefined,
23
23
  limit: typeof params.limit === "number" ? params.limit : undefined,
24
- }, session.agentId, session);
24
+ }, session.actorPeerId, session);
25
25
  },
26
26
  }), { name: "ov_search" });
27
27
  deps.registerTool((ctx) => ({
@@ -41,7 +41,7 @@ export function registerOpenVikingQueryTools(deps) {
41
41
  const session = deps.resolvePluginSessionRouting(ctx);
42
42
  return deps.readOpenVikingContent({
43
43
  uri: String(params.uri ?? ""),
44
- }, session.agentId);
44
+ }, session.actorPeerId);
45
45
  },
46
46
  }), { name: "ov_read" });
47
47
  deps.registerTool((ctx) => ({
@@ -62,7 +62,7 @@ export function registerOpenVikingQueryTools(deps) {
62
62
  const uris = Array.isArray(params.uris)
63
63
  ? params.uris.map((uri) => String(uri))
64
64
  : [];
65
- return deps.multiReadOpenVikingContent({ uris }, session.agentId);
65
+ return deps.multiReadOpenVikingContent({ uris }, session.actorPeerId);
66
66
  },
67
67
  }), { name: "ov_multi_read" });
68
68
  deps.registerTool((ctx) => ({
@@ -85,7 +85,7 @@ export function registerOpenVikingQueryTools(deps) {
85
85
  recursive: typeof params.recursive === "boolean" ? params.recursive : undefined,
86
86
  simple: typeof params.simple === "boolean" ? params.simple : undefined,
87
87
  limit: typeof params.limit === "number" ? params.limit : undefined,
88
- }, session.agentId);
88
+ }, session.actorPeerId);
89
89
  },
90
90
  }), { name: "ov_list" });
91
91
  }
@@ -157,11 +157,11 @@ export function createOpenVikingRecallTraceRuntime(deps) {
157
157
  for (const fallbackQuery of fallbackIdentities) {
158
158
  const fallback = await deps.traceRecorder.queryWithFallback(fallbackQuery);
159
159
  if (fallback.entries.length > 0) {
160
- return enrichTraceEntriesWithContent(fallback, shouldIncludeTraceContent(input), session.agentId);
160
+ return enrichTraceEntriesWithContent(fallback, shouldIncludeTraceContent(input), session.actorPeerId);
161
161
  }
162
162
  }
163
163
  }
164
- return enrichTraceEntriesWithContent(base, shouldIncludeTraceContent(input), session.agentId);
164
+ return enrichTraceEntriesWithContent(base, shouldIncludeTraceContent(input), session.actorPeerId);
165
165
  };
166
166
  const handleUriDetail = async (request) => {
167
167
  const query = toQueryObject(request);
@@ -206,7 +206,7 @@ export function createOpenVikingRecallTraceRuntime(deps) {
206
206
  }
207
207
  try {
208
208
  const client = await deps.getClient();
209
- const content = await client.read(uri, toString(query.agentId) ?? session.agentId);
209
+ const content = await client.read(uri, session.actorPeerId);
210
210
  const text = typeof content === "string" ? content : JSON.stringify(content, null, 2);
211
211
  const chars = Array.from(text);
212
212
  const slice = chars.slice(offset, offset + contentLimit).join("");
@@ -1,5 +1,6 @@
1
- import { createSessionAgentResolver, openClawSessionToOvStorageId, } from "../routing/identity-routing.js";
1
+ import { createSessionAgentResolver, openClawSessionToOvStorageId, resolveOpenVikingActorPeerId, sanitizeOpenVikingPeerId, } from "../routing/identity-routing.js";
2
2
  export function createOpenVikingSessionRoutingRuntime(options) {
3
+ const peerRole = options.peerRole ?? "assistant";
3
4
  const sessionAgentResolver = createSessionAgentResolver(options.peerPrefix);
4
5
  const rememberSessionAgentId = (ctx) => {
5
6
  sessionAgentResolver.remember(ctx);
@@ -39,11 +40,17 @@ export function createOpenVikingSessionRoutingRuntime(options) {
39
40
  ovSessionId: ovSessionId || undefined,
40
41
  };
41
42
  rememberSessionAgentId(session);
43
+ const agentId = resolveAgentId(session.sessionId, session.sessionKey, session.ovSessionId);
42
44
  return {
43
45
  sessionId: session.sessionId,
44
46
  sessionKey: session.sessionKey,
45
47
  ovSessionId: session.ovSessionId,
46
- agentId: resolveAgentId(session.sessionId, session.sessionKey, session.ovSessionId),
48
+ agentId,
49
+ actorPeerId: resolveOpenVikingActorPeerId({
50
+ peerRole,
51
+ personPeerId: sanitizeOpenVikingPeerId(ctx?.requesterSenderId ?? ctx?.senderId),
52
+ assistantPeerId: agentId,
53
+ }),
47
54
  };
48
55
  };
49
56
  const toQueryConfigContext = (session) => ({
@@ -83,7 +83,7 @@ export function registerOpenVikingToolResultTools({ registerTool, getClient, res
83
83
  }
84
84
  try {
85
85
  const client = await getClient();
86
- const result = await client.readToolResult(session.ovSessionId, parsed.toolResultId, { offset, limit, includeMetadata: true }, session.agentId);
86
+ const result = await client.readToolResult(session.ovSessionId, parsed.toolResultId, { offset, limit, includeMetadata: true });
87
87
  const returnedChars = result.content.length;
88
88
  const nextOffset = result.offset + returnedChars;
89
89
  const text = result.content || "(empty tool result chunk)";
@@ -167,7 +167,7 @@ export function registerOpenVikingToolResultTools({ registerTool, getClient, res
167
167
  const contextChars = Math.max(0, getOptionalInteger(params.context_chars ?? params.contextChars, 300));
168
168
  try {
169
169
  const client = await getClient();
170
- const result = await client.searchToolResult(session.ovSessionId, parsed.toolResultId, query, { limit, contextChars }, session.agentId);
170
+ const result = await client.searchToolResult(session.ovSessionId, parsed.toolResultId, query, { limit, contextChars });
171
171
  const matches = result.matches ?? [];
172
172
  const text = matches.length
173
173
  ? [
@@ -227,7 +227,7 @@ export function registerOpenVikingToolResultTools({ registerTool, getClient, res
227
227
  const limit = getPositiveInteger(params.limit, 50);
228
228
  try {
229
229
  const client = await getClient();
230
- const result = await client.listToolResults(session.ovSessionId, { toolName, limit }, session.agentId);
230
+ const result = await client.listToolResults(session.ovSessionId, { toolName, limit });
231
231
  const items = result.tool_results ?? [];
232
232
  const text = items.length
233
233
  ? [
@@ -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, agentId, logger, sessionLabel) {
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, agentId).catch((e) => {
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, resolveAgentId, rememberSessionAgentId, isBypassedSession, }) {
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);
@@ -248,6 +246,11 @@ export async function assembleOpenVikingSession({ sessionId, sessionKey, message
248
246
  const client = await getClient();
249
247
  const routingRef = sessionId ?? sessionKey ?? ovSessionId;
250
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
+ });
251
254
  const queryConfig = await queryConfigStore?.getEffective({
252
255
  agentId,
253
256
  sessionId,
@@ -259,6 +262,7 @@ export async function assembleOpenVikingSession({ sessionId, sessionKey, message
259
262
  queryConfig,
260
263
  client,
261
264
  agentId,
265
+ actorPeerId,
262
266
  queryText: recallQuery.query,
263
267
  logger,
264
268
  verbose: (message) => logger.info(message),
@@ -307,9 +311,7 @@ export async function assembleOpenVikingSession({ sessionId, sessionKey, message
307
311
  }
308
312
  try {
309
313
  const client = await getClient();
310
- const routingRef = sessionId ?? sessionKey ?? ovSessionId;
311
- const agentId = resolveAgentId(routingRef, sessionKey, ovSessionId);
312
- const ctx = await client.getSessionContext(ovSessionId, tokenBudget, agentId);
314
+ const ctx = await client.getSessionContext(ovSessionId, tokenBudget);
313
315
  const preAbstracts = ctx?.pre_archive_abstracts ?? [];
314
316
  const hasArchives = !!ctx?.latest_archive_overview || preAbstracts.length > 0;
315
317
  const activeCount = ctx?.messages?.length ?? 0;
@@ -596,10 +598,15 @@ export async function afterTurnOpenVikingSession({ sessionId, sessionKey, messag
596
598
  };
597
599
  });
598
600
  if (ovParts.length > 0) {
599
- await client.addSessionMessage(ovSessionId, msg.role, ovParts, agentId, createdAt, msg.role === "user" ? senderRoleId : undefined);
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
+ }));
600
607
  }
601
608
  }
602
- const session = await client.getSession(ovSessionId, agentId);
609
+ const session = await client.getSession(ovSessionId);
603
610
  const pendingTokens = session.pending_tokens ?? 0;
604
611
  const commitTokenThreshold = Math.floor(tokenBudget * cfg.commitTokenThresholdRatio);
605
612
  if (pendingTokens < commitTokenThreshold) {
@@ -616,7 +623,6 @@ export async function afterTurnOpenVikingSession({ sessionId, sessionKey, messag
616
623
  }
617
624
  const commitResult = await client.commitSession(ovSessionId, {
618
625
  wait: false,
619
- agentId,
620
626
  keepRecentCount: cfg.commitKeepRecentCount,
621
627
  });
622
628
  logger.info(`openviking: committed session=${ovSessionId}, ` +
@@ -637,7 +643,7 @@ export async function afterTurnOpenVikingSession({ sessionId, sessionKey, messag
637
643
  if (commitResult.task_id && cfg.logFindRequests) {
638
644
  logger.info(`openviking: Phase2 memory extraction runs asynchronously on the server (task_id=${commitResult.task_id}). ` +
639
645
  "memories_extracted appears only after that task completes — not in this immediate response.");
640
- void pollPhase2ExtractionOutcome(client, commitResult.task_id, agentId, logger, ovSessionId);
646
+ void pollPhase2ExtractionOutcome(client, commitResult.task_id, logger, ovSessionId);
641
647
  }
642
648
  }
643
649
  catch (err) {
@@ -697,7 +703,7 @@ export async function compactOpenVikingSession({ sessionId, sessionKey, tokenBud
697
703
  let preCommitEstimatedTokens;
698
704
  if (typeof tokensBeforeOriginal !== "number") {
699
705
  try {
700
- const preCtx = await client.getSessionContext(ovSessionId, tokenBudget, agentId);
706
+ const preCtx = await client.getSessionContext(ovSessionId, tokenBudget);
701
707
  if (typeof preCtx.estimatedTokens === "number" && Number.isFinite(preCtx.estimatedTokens)) {
702
708
  preCommitEstimatedTokens = preCtx.estimatedTokens;
703
709
  }
@@ -712,7 +718,6 @@ export async function compactOpenVikingSession({ sessionId, sessionKey, tokenBud
712
718
  logger.info(`openviking: compact committing session=${ovSessionId} (wait=true, tokenBudget=${tokenBudget})`);
713
719
  const commitResult = await client.commitSession(ovSessionId, {
714
720
  wait: true,
715
- agentId,
716
721
  keepRecentCount: 0,
717
722
  });
718
723
  const memCount = totalExtractedMemories(commitResult.memories_extracted);
@@ -774,7 +779,7 @@ export async function compactOpenVikingSession({ sessionId, sessionKey, tokenBud
774
779
  let tokensAfter;
775
780
  let contextFetchError;
776
781
  try {
777
- const ctx = await client.getSessionContext(ovSessionId, tokenBudget, agentId);
782
+ const ctx = await client.getSessionContext(ovSessionId, tokenBudget);
778
783
  logger.info(`openviking: compact getSessionContext raw result for ${ovSessionId}: ` +
779
784
  JSON.stringify(ctx, null, 2));
780
785
  if (typeof ctx.latest_archive_overview === "string") {
@@ -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
- if (!senderId) {
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/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, toRoleId } from "./services/context-message-adapter.js";
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
- extractSenderId: extractToolSenderId,
333
- toRoleId,
333
+ peerRole: cfg.peer_role,
334
334
  resolvePluginSessionRouting,
335
335
  isBypassedSession,
336
336
  makeBypassedToolResult,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openviking/openclaw-plugin",
3
- "version": "2026.7.11",
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",
@@ -5,6 +5,8 @@ export type PluginCommandContext = {
5
5
  sessionId?: string;
6
6
  agentId?: string;
7
7
  ovSessionId?: string;
8
+ senderId?: string;
9
+ requesterSenderId?: string;
8
10
  };
9
11
 
10
12
  export type CommandResult = {