@openviking/openclaw-plugin 2026.7.11 → 2026.8.6-1
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-ZH.md +39 -0
- package/INSTALL.md +39 -0
- package/README.md +5 -0
- package/auto-recall.ts +5 -4
- package/config.ts +121 -6
- package/context-engine.ts +0 -1
- package/dist/auto-recall.js +4 -4
- package/dist/config.js +78 -4
- package/dist/context-engine.js +0 -1
- package/dist/index.js +13 -4
- package/dist/plugin/openviking-archive-tools.js +1 -2
- package/dist/plugin/openviking-command-definitions.js +3 -3
- package/dist/plugin/openviking-experience-tools.js +105 -0
- 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 +11 -6
- 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/routing/identity-routing.js +27 -0
- package/dist/routing/memory-uri.js +1 -0
- package/dist/services/context-lifecycle-service.js +21 -16
- package/dist/services/context-message-adapter.js +2 -9
- package/index.ts +13 -3
- package/install-manifest.json +1 -0
- package/openclaw.plugin.json +34 -3
- package/package.json +1 -1
- package/plugin/command-registration.ts +2 -0
- package/plugin/openviking-archive-tools.ts +1 -4
- package/plugin/openviking-command-definitions.ts +4 -3
- package/plugin/openviking-experience-tools.ts +160 -0
- package/plugin/openviking-import-tools.ts +4 -2
- package/plugin/openviking-memory-recall-tools.ts +6 -4
- package/plugin/openviking-memory-tools.ts +18 -10
- 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/registries/openviking-tools.ts +6 -0
- package/routing/identity-routing.ts +41 -0
- package/routing/memory-uri.ts +1 -0
- package/services/context-lifecycle-service.ts +27 -18
- package/services/context-message-adapter.ts +2 -9
- package/skills/ov-experience-memory/SKILL.md +156 -0
package/openclaw.plugin.json
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
"name": "OpenViking",
|
|
4
4
|
"kind": "context-engine",
|
|
5
5
|
"description": "OpenClaw context-engine plugin for memory management — powered by OpenViking",
|
|
6
|
+
"icon": "https://raw.githubusercontent.com/volcengine/OpenViking/main/docs/images/ov-logo-icon.png",
|
|
6
7
|
"activation": {
|
|
7
8
|
"onStartup": true,
|
|
8
9
|
"onCapabilities": [
|
|
@@ -12,11 +13,14 @@
|
|
|
12
13
|
},
|
|
13
14
|
"contracts": {
|
|
14
15
|
"tools": [
|
|
16
|
+
"add_resource",
|
|
15
17
|
"add_skill",
|
|
16
18
|
"ov_search",
|
|
17
19
|
"ov_read",
|
|
18
20
|
"ov_multi_read",
|
|
19
21
|
"ov_list",
|
|
22
|
+
"search_experience",
|
|
23
|
+
"read_experience",
|
|
20
24
|
"memory_recall",
|
|
21
25
|
"ov_recall_trace",
|
|
22
26
|
"memory_store",
|
|
@@ -96,7 +100,7 @@
|
|
|
96
100
|
"label": "OpenViking API Key",
|
|
97
101
|
"sensitive": true,
|
|
98
102
|
"placeholder": "${OPENVIKING_API_KEY}",
|
|
99
|
-
"help": "Optional API key for OpenViking server"
|
|
103
|
+
"help": "Optional API key for OpenViking server. Accepts a plain string, ${ENV_VAR} interpolation, or a SecretRef object ({source: env/file, id}). Prefer a SecretRef so the key is never stored as plaintext in openclaw.json."
|
|
100
104
|
},
|
|
101
105
|
"headers": {
|
|
102
106
|
"label": "Headers",
|
|
@@ -250,7 +254,7 @@
|
|
|
250
254
|
"enabledTools": {
|
|
251
255
|
"label": "Enabled Tools",
|
|
252
256
|
"placeholder": "default",
|
|
253
|
-
"help": "Agent-visible tool allowlist. Accepts tool names or groups: default, all, memory, resource_query, import, recall_trace, archive, tool_result. add_resource also requires enableAddResourceTool=true.",
|
|
257
|
+
"help": "Agent-visible tool allowlist. Accepts tool names or groups: default, all, memory, resource_query, experience, import, recall_trace, archive, tool_result. add_resource also requires enableAddResourceTool=true.",
|
|
254
258
|
"advanced": true
|
|
255
259
|
},
|
|
256
260
|
"disabledTools": {
|
|
@@ -290,7 +294,34 @@
|
|
|
290
294
|
"description": "Deprecated and ignored. Tenant identity headers are controlled by explicit accountId/userId."
|
|
291
295
|
},
|
|
292
296
|
"apiKey": {
|
|
293
|
-
"
|
|
297
|
+
"oneOf": [
|
|
298
|
+
{
|
|
299
|
+
"type": "string",
|
|
300
|
+
"description": "Plain API key or ${ENV_VAR} interpolation. For secret-manager-managed keys, prefer the SecretRef object shape below."
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
"type": "object",
|
|
304
|
+
"title": "SecretRef (env)",
|
|
305
|
+
"description": "Read the API key from an environment variable.",
|
|
306
|
+
"additionalProperties": false,
|
|
307
|
+
"required": ["source", "id"],
|
|
308
|
+
"properties": {
|
|
309
|
+
"source": { "type": "string", "const": "env", "description": "Look up the key in process.env[id] at plugin load time. Equivalent to ${ENV} interpolation but declarative and visible in the UI." },
|
|
310
|
+
"id": { "type": "string", "description": "Name of the environment variable. Example: OPENVIKING_API_KEY." }
|
|
311
|
+
}
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
"type": "object",
|
|
315
|
+
"title": "SecretRef (file)",
|
|
316
|
+
"description": "Read the API key from a file on disk, e.g. a Kubernetes secretKeyRef mount volume or a 0600-permission managed file. ~ is expanded and trailing whitespace is trimmed.",
|
|
317
|
+
"additionalProperties": false,
|
|
318
|
+
"required": ["source", "id"],
|
|
319
|
+
"properties": {
|
|
320
|
+
"source": { "type": "string", "const": "file" },
|
|
321
|
+
"id": { "type": "string", "description": "Absolute file path or ~/relative path. Example: /etc/secrets/openviking.key." }
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
]
|
|
294
325
|
},
|
|
295
326
|
"headers": {
|
|
296
327
|
"type": "object",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openviking/openclaw-plugin",
|
|
3
|
-
"version": "2026.
|
|
3
|
+
"version": "2026.8.6-1",
|
|
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",
|
|
@@ -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 = [
|
|
@@ -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
|
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { Type } from "@sinclair/typebox";
|
|
2
|
+
|
|
3
|
+
import type { FindResult } from "../client.js";
|
|
4
|
+
|
|
5
|
+
const EXPERIENCE_TARGET_URI = "viking://user/memories/experiences/";
|
|
6
|
+
const DEFAULT_LIMIT = 5;
|
|
7
|
+
const MAX_LIMIT = 20;
|
|
8
|
+
const EXPERIENCE_SIDECAR_FILENAMES = new Set([".abstract.md", ".overview.md", ".relations.json"]);
|
|
9
|
+
|
|
10
|
+
export type OpenVikingExperienceToolContext = {
|
|
11
|
+
sessionKey?: string;
|
|
12
|
+
sessionId?: string;
|
|
13
|
+
agentId?: string;
|
|
14
|
+
senderId?: string;
|
|
15
|
+
requesterSenderId?: string;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export type OpenVikingExperienceSession = {
|
|
19
|
+
sessionId?: string;
|
|
20
|
+
sessionKey?: string;
|
|
21
|
+
ovSessionId?: string;
|
|
22
|
+
agentId: string;
|
|
23
|
+
actorPeerId?: string;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
type OpenVikingExperienceClient = {
|
|
27
|
+
find: (
|
|
28
|
+
query: string,
|
|
29
|
+
options: { targetUri: string; limit: number; scoreThreshold?: number },
|
|
30
|
+
actorPeerId?: string,
|
|
31
|
+
) => Promise<FindResult>;
|
|
32
|
+
read: (uri: string, actorPeerId?: string) => Promise<unknown>;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export type OpenVikingExperienceToolsDeps = {
|
|
36
|
+
registerTool: (toolOrFactory: unknown, opts: { name: string }) => void;
|
|
37
|
+
getClient: () => Promise<OpenVikingExperienceClient>;
|
|
38
|
+
resolvePluginSessionRouting: (ctx?: OpenVikingExperienceToolContext) => OpenVikingExperienceSession;
|
|
39
|
+
isBypassedSession: (ctx?: OpenVikingExperienceToolContext) => boolean;
|
|
40
|
+
makeBypassedToolResult: (toolName: string) => unknown;
|
|
41
|
+
userId?: string;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
function clampLimit(value: unknown): number {
|
|
45
|
+
const parsed = Number.isFinite(Number(value)) ? Math.trunc(Number(value)) : DEFAULT_LIMIT;
|
|
46
|
+
return Math.max(1, Math.min(MAX_LIMIT, parsed));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function experienceRelativePath(uri: string): string {
|
|
50
|
+
return uri.match(/^viking:\/\/user\/[^/]+\/memories\/experiences\/(.+)$/)?.[1] ?? "";
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function isCanonicalExperienceUri(uri: unknown, expectedUserId?: string): uri is string {
|
|
54
|
+
const value = typeof uri === "string" ? uri.trim() : "";
|
|
55
|
+
if (!value || value.includes("?") || value.includes("#")) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
const owner = value.match(/^viking:\/\/user\/([^/]+)\//)?.[1] ?? "";
|
|
59
|
+
if (expectedUserId?.trim() && owner !== expectedUserId.trim()) {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
const relative = experienceRelativePath(value);
|
|
63
|
+
const segments = relative.split("/");
|
|
64
|
+
const basename = segments.at(-1) ?? "";
|
|
65
|
+
return Boolean(
|
|
66
|
+
relative
|
|
67
|
+
&& segments.every((segment) => segment && segment !== "." && segment !== "..")
|
|
68
|
+
&& !EXPERIENCE_SIDECAR_FILENAMES.has(basename)
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function titleFromUri(uri: string): string {
|
|
73
|
+
const basename = uri.split("/").at(-1) ?? uri;
|
|
74
|
+
const title = basename.replace(/\.md$/i, "");
|
|
75
|
+
try {
|
|
76
|
+
return decodeURIComponent(title);
|
|
77
|
+
} catch {
|
|
78
|
+
return title;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function jsonToolResult(payload: Record<string, unknown>): {
|
|
83
|
+
content: Array<{ type: "text"; text: string }>;
|
|
84
|
+
} {
|
|
85
|
+
return {
|
|
86
|
+
content: [{ type: "text", text: JSON.stringify(payload) }],
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function registerOpenVikingExperienceTools(deps: OpenVikingExperienceToolsDeps): void {
|
|
91
|
+
deps.registerTool(
|
|
92
|
+
(ctx: OpenVikingExperienceToolContext) => ({
|
|
93
|
+
name: "search_experience",
|
|
94
|
+
label: "Search Experience (OpenViking)",
|
|
95
|
+
description:
|
|
96
|
+
"Search reusable task-execution experiences for the current OpenViking user. " +
|
|
97
|
+
"Use before executing operational or multi-step tasks, then call read_experience for selected results.",
|
|
98
|
+
parameters: Type.Object({
|
|
99
|
+
query: Type.String({ description: "Current task, situation, or execution problem to search for" }),
|
|
100
|
+
limit: Type.Optional(Type.Integer({
|
|
101
|
+
description: `Maximum results to return. Default: ${DEFAULT_LIMIT}; maximum: ${MAX_LIMIT}`,
|
|
102
|
+
minimum: 1,
|
|
103
|
+
maximum: MAX_LIMIT,
|
|
104
|
+
})),
|
|
105
|
+
}, { additionalProperties: false }),
|
|
106
|
+
async execute(_toolCallId: string, params: Record<string, unknown>) {
|
|
107
|
+
if (deps.isBypassedSession(ctx)) {
|
|
108
|
+
return deps.makeBypassedToolResult("search_experience");
|
|
109
|
+
}
|
|
110
|
+
const query = typeof params.query === "string" ? params.query.trim() : "";
|
|
111
|
+
if (!query) {
|
|
112
|
+
throw new Error("query is required");
|
|
113
|
+
}
|
|
114
|
+
const session = deps.resolvePluginSessionRouting(ctx);
|
|
115
|
+
const client = await deps.getClient();
|
|
116
|
+
const result = await client.find(query, {
|
|
117
|
+
targetUri: EXPERIENCE_TARGET_URI,
|
|
118
|
+
limit: clampLimit(params.limit),
|
|
119
|
+
}, session.actorPeerId);
|
|
120
|
+
const results = (result.memories ?? [])
|
|
121
|
+
.filter((item) => isCanonicalExperienceUri(item.uri, deps.userId))
|
|
122
|
+
.map((item) => ({
|
|
123
|
+
uri: item.uri,
|
|
124
|
+
title: titleFromUri(item.uri),
|
|
125
|
+
score: typeof item.score === "number" ? item.score : 0,
|
|
126
|
+
snippet: item.abstract || item.overview || "",
|
|
127
|
+
}));
|
|
128
|
+
return jsonToolResult({ results });
|
|
129
|
+
},
|
|
130
|
+
}),
|
|
131
|
+
{ name: "search_experience" },
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
deps.registerTool(
|
|
135
|
+
(ctx: OpenVikingExperienceToolContext) => ({
|
|
136
|
+
name: "read_experience",
|
|
137
|
+
label: "Read Experience (OpenViking)",
|
|
138
|
+
description:
|
|
139
|
+
"Read the complete Markdown body of one Experience returned by search_experience and use it as execution guidance.",
|
|
140
|
+
parameters: Type.Object({
|
|
141
|
+
uri: Type.String({ description: "Exact canonical Experience URI returned by search_experience" }),
|
|
142
|
+
}, { additionalProperties: false }),
|
|
143
|
+
async execute(_toolCallId: string, params: Record<string, unknown>) {
|
|
144
|
+
if (deps.isBypassedSession(ctx)) {
|
|
145
|
+
return deps.makeBypassedToolResult("read_experience");
|
|
146
|
+
}
|
|
147
|
+
const uri = typeof params.uri === "string" ? params.uri.trim() : "";
|
|
148
|
+
if (!isCanonicalExperienceUri(uri, deps.userId)) {
|
|
149
|
+
throw new Error("uri must be a canonical OpenViking Experience URI returned by search_experience");
|
|
150
|
+
}
|
|
151
|
+
const session = deps.resolvePluginSessionRouting(ctx);
|
|
152
|
+
const client = await deps.getClient();
|
|
153
|
+
const content = await client.read(uri, session.actorPeerId);
|
|
154
|
+
const text = typeof content === "string" ? content : JSON.stringify(content, null, 2);
|
|
155
|
+
return jsonToolResult({ uri, content: text });
|
|
156
|
+
},
|
|
157
|
+
}),
|
|
158
|
+
{ name: "read_experience" },
|
|
159
|
+
);
|
|
160
|
+
}
|
|
@@ -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,
|
|
@@ -2,6 +2,10 @@ import { Type } from "@sinclair/typebox";
|
|
|
2
2
|
|
|
3
3
|
import type { CommitSessionResult, FindResultItem } from "../client.js";
|
|
4
4
|
import { clampScore, postProcessMemories } from "../memory-ranking.js";
|
|
5
|
+
import {
|
|
6
|
+
resolveOpenVikingMessagePeerId,
|
|
7
|
+
type OpenVikingPeerRole,
|
|
8
|
+
} from "../routing/identity-routing.js";
|
|
5
9
|
import { isMemoryUri } from "../routing/memory-uri.js";
|
|
6
10
|
|
|
7
11
|
export type OpenVikingMemoryToolContext = {
|
|
@@ -17,6 +21,7 @@ export type OpenVikingMemorySession = {
|
|
|
17
21
|
sessionKey?: string;
|
|
18
22
|
ovSessionId?: string;
|
|
19
23
|
agentId: string;
|
|
24
|
+
actorPeerId?: string;
|
|
20
25
|
};
|
|
21
26
|
|
|
22
27
|
export type OpenVikingMemoryClient = {
|
|
@@ -30,7 +35,7 @@ export type OpenVikingMemoryClient = {
|
|
|
30
35
|
) => Promise<void>;
|
|
31
36
|
commitSession: (
|
|
32
37
|
sessionId: string,
|
|
33
|
-
options: { wait: true; agentId
|
|
38
|
+
options: { wait: true; agentId?: string; keepRecentCount: number },
|
|
34
39
|
) => Promise<CommitSessionResult>;
|
|
35
40
|
deleteUri: (uri: string, agentId?: string) => Promise<void>;
|
|
36
41
|
find: (
|
|
@@ -45,8 +50,7 @@ export type OpenVikingMemoryToolsDeps = {
|
|
|
45
50
|
getClient: () => Promise<OpenVikingMemoryClient>;
|
|
46
51
|
normalizeSessionId: (sessionId: string) => string;
|
|
47
52
|
createTempSessionId: () => string;
|
|
48
|
-
|
|
49
|
-
toRoleId: (senderId?: string) => string | undefined;
|
|
53
|
+
peerRole: OpenVikingPeerRole;
|
|
50
54
|
resolvePluginSessionRouting: (ctx?: OpenVikingMemoryToolContext) => OpenVikingMemorySession;
|
|
51
55
|
isBypassedSession: (ctx?: OpenVikingMemoryToolContext) => boolean;
|
|
52
56
|
makeBypassedToolResult: (toolName: string) => unknown;
|
|
@@ -107,18 +111,22 @@ export function registerOpenVikingMemoryTools(deps: OpenVikingMemoryToolsDeps):
|
|
|
107
111
|
sessionId = deps.createTempSessionId();
|
|
108
112
|
usedTempSession = true;
|
|
109
113
|
}
|
|
110
|
-
const
|
|
114
|
+
const peerId = resolveOpenVikingMessagePeerId({
|
|
115
|
+
peerRole: deps.peerRole,
|
|
116
|
+
role,
|
|
117
|
+
personPeerId: session.actorPeerId,
|
|
118
|
+
assistantPeerId: session.actorPeerId,
|
|
119
|
+
});
|
|
111
120
|
await client.addSessionMessage(
|
|
112
121
|
sessionId,
|
|
113
122
|
role,
|
|
114
123
|
[{ type: "text", text }],
|
|
115
|
-
session.agentId,
|
|
116
124
|
undefined,
|
|
117
|
-
|
|
125
|
+
undefined,
|
|
126
|
+
peerId,
|
|
118
127
|
);
|
|
119
128
|
const commitResult = await client.commitSession(sessionId, {
|
|
120
129
|
wait: true,
|
|
121
|
-
agentId: session.agentId,
|
|
122
130
|
keepRecentCount: 0,
|
|
123
131
|
});
|
|
124
132
|
const memoriesCount = totalCommitMemories(commitResult);
|
|
@@ -216,7 +224,7 @@ export function registerOpenVikingMemoryTools(deps: OpenVikingMemoryToolsDeps):
|
|
|
216
224
|
details: { action: "rejected", uri },
|
|
217
225
|
};
|
|
218
226
|
}
|
|
219
|
-
await client.deleteUri(uri, session.
|
|
227
|
+
await client.deleteUri(uri, session.actorPeerId);
|
|
220
228
|
return {
|
|
221
229
|
content: [{ type: "text", text: `Forgotten: ${uri}` }],
|
|
222
230
|
details: { action: "deleted", uri },
|
|
@@ -252,7 +260,7 @@ export function registerOpenVikingMemoryTools(deps: OpenVikingMemoryToolsDeps):
|
|
|
252
260
|
limit: requestLimit,
|
|
253
261
|
scoreThreshold: 0,
|
|
254
262
|
},
|
|
255
|
-
session.
|
|
263
|
+
session.actorPeerId,
|
|
256
264
|
);
|
|
257
265
|
const candidates = postProcessMemories(result.memories ?? [], {
|
|
258
266
|
limit: requestLimit,
|
|
@@ -272,7 +280,7 @@ export function registerOpenVikingMemoryTools(deps: OpenVikingMemoryToolsDeps):
|
|
|
272
280
|
}
|
|
273
281
|
const top = candidates[0];
|
|
274
282
|
if (candidates.length === 1 && clampScore(top.score) >= 0.85) {
|
|
275
|
-
await client.deleteUri(top.uri, session.
|
|
283
|
+
await client.deleteUri(top.uri, session.actorPeerId);
|
|
276
284
|
return {
|
|
277
285
|
content: [{ type: "text", text: `Forgotten: ${top.uri}` }],
|
|
278
286
|
details: { action: "deleted", uri: top.uri, score: top.score ?? 0 },
|
|
@@ -445,7 +445,7 @@ export function createOpenVikingQueryRuntime<TQueryConfigContext>(deps: OpenViki
|
|
|
445
445
|
sessionId: traceCtx?.sessionId,
|
|
446
446
|
sessionKey: traceCtx?.sessionKey,
|
|
447
447
|
ovSessionId: traceCtx?.ovSessionId,
|
|
448
|
-
agentId,
|
|
448
|
+
agentId: traceCtx?.agentId,
|
|
449
449
|
source: "ov_search",
|
|
450
450
|
operationType: "semantic_find",
|
|
451
451
|
resourceTypes: [...new Set(searches.map((search) => search.resourceType).filter((resourceType): resourceType is RecallResourceType => resourceType !== "archive"))],
|
|
@@ -5,6 +5,7 @@ export type OpenVikingQueryToolContext = {
|
|
|
5
5
|
sessionId?: string;
|
|
6
6
|
agentId?: string;
|
|
7
7
|
senderId?: string;
|
|
8
|
+
requesterSenderId?: string;
|
|
8
9
|
};
|
|
9
10
|
|
|
10
11
|
export type OpenVikingQuerySession = {
|
|
@@ -12,6 +13,7 @@ export type OpenVikingQuerySession = {
|
|
|
12
13
|
sessionKey?: string;
|
|
13
14
|
ovSessionId?: string;
|
|
14
15
|
agentId: string;
|
|
16
|
+
actorPeerId?: string;
|
|
15
17
|
};
|
|
16
18
|
|
|
17
19
|
export type OVSearchInput = {
|
|
@@ -74,7 +76,7 @@ export function registerOpenVikingQueryTools(deps: OpenVikingQueryToolsDeps): vo
|
|
|
74
76
|
query: String((params as { query?: unknown }).query ?? ""),
|
|
75
77
|
uri: typeof params.uri === "string" ? params.uri : undefined,
|
|
76
78
|
limit: typeof params.limit === "number" ? params.limit : undefined,
|
|
77
|
-
}, session.
|
|
79
|
+
}, session.actorPeerId, session);
|
|
78
80
|
},
|
|
79
81
|
}),
|
|
80
82
|
{ name: "ov_search" },
|
|
@@ -99,7 +101,7 @@ export function registerOpenVikingQueryTools(deps: OpenVikingQueryToolsDeps): vo
|
|
|
99
101
|
const session = deps.resolvePluginSessionRouting(ctx);
|
|
100
102
|
return deps.readOpenVikingContent({
|
|
101
103
|
uri: String((params as { uri?: unknown }).uri ?? ""),
|
|
102
|
-
}, session.
|
|
104
|
+
}, session.actorPeerId);
|
|
103
105
|
},
|
|
104
106
|
}),
|
|
105
107
|
{ name: "ov_read" },
|
|
@@ -125,7 +127,7 @@ export function registerOpenVikingQueryTools(deps: OpenVikingQueryToolsDeps): vo
|
|
|
125
127
|
const uris = Array.isArray((params as { uris?: unknown }).uris)
|
|
126
128
|
? (params as { uris: unknown[] }).uris.map((uri) => String(uri))
|
|
127
129
|
: [];
|
|
128
|
-
return deps.multiReadOpenVikingContent({ uris }, session.
|
|
130
|
+
return deps.multiReadOpenVikingContent({ uris }, session.actorPeerId);
|
|
129
131
|
},
|
|
130
132
|
}),
|
|
131
133
|
{ name: "ov_multi_read" },
|
|
@@ -153,7 +155,7 @@ export function registerOpenVikingQueryTools(deps: OpenVikingQueryToolsDeps): vo
|
|
|
153
155
|
recursive: typeof params.recursive === "boolean" ? params.recursive : undefined,
|
|
154
156
|
simple: typeof params.simple === "boolean" ? params.simple : undefined,
|
|
155
157
|
limit: typeof params.limit === "number" ? params.limit : undefined,
|
|
156
|
-
}, session.
|
|
158
|
+
}, session.actorPeerId);
|
|
157
159
|
},
|
|
158
160
|
}),
|
|
159
161
|
{ name: "ov_list" },
|
|
@@ -26,6 +26,7 @@ export type OpenVikingRecallTraceSession = {
|
|
|
26
26
|
sessionKey?: string;
|
|
27
27
|
ovSessionId?: string;
|
|
28
28
|
agentId: string;
|
|
29
|
+
actorPeerId?: string;
|
|
29
30
|
};
|
|
30
31
|
|
|
31
32
|
export type OpenVikingRecallTraceQueryResult = {
|
|
@@ -248,11 +249,11 @@ export function createOpenVikingRecallTraceRuntime(deps: OpenVikingRecallTraceRu
|
|
|
248
249
|
for (const fallbackQuery of fallbackIdentities) {
|
|
249
250
|
const fallback = await deps.traceRecorder.queryWithFallback(fallbackQuery);
|
|
250
251
|
if (fallback.entries.length > 0) {
|
|
251
|
-
return enrichTraceEntriesWithContent(fallback, shouldIncludeTraceContent(input), session.
|
|
252
|
+
return enrichTraceEntriesWithContent(fallback, shouldIncludeTraceContent(input), session.actorPeerId);
|
|
252
253
|
}
|
|
253
254
|
}
|
|
254
255
|
}
|
|
255
|
-
return enrichTraceEntriesWithContent(base, shouldIncludeTraceContent(input), session.
|
|
256
|
+
return enrichTraceEntriesWithContent(base, shouldIncludeTraceContent(input), session.actorPeerId);
|
|
256
257
|
};
|
|
257
258
|
|
|
258
259
|
const handleUriDetail = async (request?: RecallTraceRouteRequest) => {
|
|
@@ -298,7 +299,7 @@ export function createOpenVikingRecallTraceRuntime(deps: OpenVikingRecallTraceRu
|
|
|
298
299
|
}
|
|
299
300
|
try {
|
|
300
301
|
const client = await deps.getClient();
|
|
301
|
-
const content = await client.read(uri,
|
|
302
|
+
const content = await client.read(uri, session.actorPeerId);
|
|
302
303
|
const text = typeof content === "string" ? content : JSON.stringify(content, null, 2);
|
|
303
304
|
const chars = Array.from(text);
|
|
304
305
|
const slice = chars.slice(offset, offset + contentLimit).join("");
|
|
@@ -11,6 +11,7 @@ export type OpenVikingRecallTraceToolContext = {
|
|
|
11
11
|
sessionId?: string;
|
|
12
12
|
agentId?: string;
|
|
13
13
|
senderId?: string;
|
|
14
|
+
requesterSenderId?: string;
|
|
14
15
|
};
|
|
15
16
|
|
|
16
17
|
export type OpenVikingRecallTraceSession = {
|
|
@@ -18,6 +19,7 @@ export type OpenVikingRecallTraceSession = {
|
|
|
18
19
|
sessionKey?: string;
|
|
19
20
|
ovSessionId?: string;
|
|
20
21
|
agentId: string;
|
|
22
|
+
actorPeerId?: string;
|
|
21
23
|
};
|
|
22
24
|
|
|
23
25
|
export type RecallTraceToolInput = {
|
|
@@ -2,6 +2,9 @@ import type { QueryConfigContext } from "../query-config.js";
|
|
|
2
2
|
import {
|
|
3
3
|
createSessionAgentResolver,
|
|
4
4
|
openClawSessionToOvStorageId,
|
|
5
|
+
resolveOpenVikingActorPeerId,
|
|
6
|
+
sanitizeOpenVikingPeerId,
|
|
7
|
+
type OpenVikingPeerRole,
|
|
5
8
|
} from "../routing/identity-routing.js";
|
|
6
9
|
|
|
7
10
|
type Logger = {
|
|
@@ -10,6 +13,8 @@ type Logger = {
|
|
|
10
13
|
|
|
11
14
|
export type SessionAgentLookup = {
|
|
12
15
|
agentId?: string;
|
|
16
|
+
senderId?: string;
|
|
17
|
+
requesterSenderId?: string;
|
|
13
18
|
sessionId?: string;
|
|
14
19
|
sessionKey?: string;
|
|
15
20
|
ovSessionId?: string;
|
|
@@ -20,13 +25,16 @@ export type PluginSessionRouting = {
|
|
|
20
25
|
sessionKey?: string;
|
|
21
26
|
ovSessionId?: string;
|
|
22
27
|
agentId: string;
|
|
28
|
+
actorPeerId?: string;
|
|
23
29
|
};
|
|
24
30
|
|
|
25
31
|
export function createOpenVikingSessionRoutingRuntime(options: {
|
|
32
|
+
peerRole?: OpenVikingPeerRole;
|
|
26
33
|
peerPrefix: string;
|
|
27
34
|
logFindRequests: boolean;
|
|
28
35
|
logger: Logger;
|
|
29
36
|
}) {
|
|
37
|
+
const peerRole = options.peerRole ?? "assistant";
|
|
30
38
|
const sessionAgentResolver = createSessionAgentResolver(options.peerPrefix);
|
|
31
39
|
|
|
32
40
|
const rememberSessionAgentId = (ctx: SessionAgentLookup) => {
|
|
@@ -81,11 +89,17 @@ export function createOpenVikingSessionRoutingRuntime(options: {
|
|
|
81
89
|
};
|
|
82
90
|
rememberSessionAgentId(session);
|
|
83
91
|
|
|
92
|
+
const agentId = resolveAgentId(session.sessionId, session.sessionKey, session.ovSessionId);
|
|
84
93
|
return {
|
|
85
94
|
sessionId: session.sessionId,
|
|
86
95
|
sessionKey: session.sessionKey,
|
|
87
96
|
ovSessionId: session.ovSessionId,
|
|
88
|
-
agentId
|
|
97
|
+
agentId,
|
|
98
|
+
actorPeerId: resolveOpenVikingActorPeerId({
|
|
99
|
+
peerRole,
|
|
100
|
+
personPeerId: sanitizeOpenVikingPeerId(ctx?.requesterSenderId ?? ctx?.senderId),
|
|
101
|
+
assistantPeerId: agentId,
|
|
102
|
+
}),
|
|
89
103
|
};
|
|
90
104
|
};
|
|
91
105
|
|