@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
|
@@ -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;
|
|
@@ -71,7 +75,7 @@ export function registerOpenVikingMemoryTools(deps: OpenVikingMemoryToolsDeps):
|
|
|
71
75
|
name: "memory_store",
|
|
72
76
|
label: "Memory Store (OpenViking)",
|
|
73
77
|
description:
|
|
74
|
-
"Store text in OpenViking memory pipeline by writing to a session and running memory extraction.",
|
|
78
|
+
"Store text in OpenViking memory pipeline by writing to a session and running memory extraction. Use when the user explicitly asks to remember, save, or store an important long-term fact, preference, project, or decision; automatic capture is threshold/commit dependent.",
|
|
75
79
|
parameters: Type.Object({
|
|
76
80
|
text: Type.String({ description: "Information to store as memory source text" }),
|
|
77
81
|
role: Type.Optional(Type.String({ description: "Session role, default user" })),
|
|
@@ -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
|
|
|
@@ -27,7 +27,6 @@ export type OpenVikingToolResultClient = {
|
|
|
27
27
|
sessionId: string,
|
|
28
28
|
toolResultId: string,
|
|
29
29
|
options: { offset: number; limit: number; includeMetadata: boolean },
|
|
30
|
-
agentId?: string,
|
|
31
30
|
) => Promise<{
|
|
32
31
|
content: string;
|
|
33
32
|
offset: number;
|
|
@@ -42,7 +41,6 @@ export type OpenVikingToolResultClient = {
|
|
|
42
41
|
toolResultId: string,
|
|
43
42
|
query: string,
|
|
44
43
|
options: { limit: number; contextChars: number },
|
|
45
|
-
agentId?: string,
|
|
46
44
|
) => Promise<{
|
|
47
45
|
tool_result_id: string;
|
|
48
46
|
matches?: Array<{ offset: number; snippet: string }>;
|
|
@@ -50,7 +48,6 @@ export type OpenVikingToolResultClient = {
|
|
|
50
48
|
listToolResults: (
|
|
51
49
|
sessionId: string,
|
|
52
50
|
options: { toolName?: string; limit: number },
|
|
53
|
-
agentId?: string,
|
|
54
51
|
) => Promise<{
|
|
55
52
|
tool_results?: Array<{
|
|
56
53
|
storage_uri?: string;
|
|
@@ -180,7 +177,6 @@ export function registerOpenVikingToolResultTools({
|
|
|
180
177
|
session.ovSessionId,
|
|
181
178
|
parsed.toolResultId,
|
|
182
179
|
{ offset, limit, includeMetadata: true },
|
|
183
|
-
session.agentId,
|
|
184
180
|
);
|
|
185
181
|
const returnedChars = result.content.length;
|
|
186
182
|
const nextOffset = result.offset + returnedChars;
|
|
@@ -281,7 +277,6 @@ export function registerOpenVikingToolResultTools({
|
|
|
281
277
|
parsed.toolResultId,
|
|
282
278
|
query,
|
|
283
279
|
{ limit, contextChars },
|
|
284
|
-
session.agentId,
|
|
285
280
|
);
|
|
286
281
|
const matches = result.matches ?? [];
|
|
287
282
|
const text = matches.length
|
|
@@ -354,7 +349,6 @@ export function registerOpenVikingToolResultTools({
|
|
|
354
349
|
const result = await client.listToolResults(
|
|
355
350
|
session.ovSessionId,
|
|
356
351
|
{ toolName, limit },
|
|
357
|
-
session.agentId,
|
|
358
352
|
);
|
|
359
353
|
const items = result.tool_results ?? [];
|
|
360
354
|
const text = items.length
|
package/query-config.ts
CHANGED
|
@@ -231,7 +231,13 @@ export class RuntimeQueryConfigStore {
|
|
|
231
231
|
const { params, warnings } = normalizeRuntimeQueryParams(patch);
|
|
232
232
|
const key = this.keyForScope(scope, ctx);
|
|
233
233
|
const bucket = scope === "claw" ? this.data.claws : this.data.sessions;
|
|
234
|
-
bucket[key]
|
|
234
|
+
const previous = bucket[key];
|
|
235
|
+
bucket[key] = {
|
|
236
|
+
params: { ...(previous?.params ?? {}), ...params },
|
|
237
|
+
updatedAt: Date.now(),
|
|
238
|
+
updatedBy: "command",
|
|
239
|
+
peerId: ctx.peerId,
|
|
240
|
+
};
|
|
235
241
|
this.data.updatedAt = Date.now();
|
|
236
242
|
await this.persist();
|
|
237
243
|
return { warnings };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export type OpenVikingRequestHeaders = Record<string, string>;
|
|
2
|
+
|
|
3
|
+
export type ResolveOpenVikingRequestHeadersOptions = {
|
|
4
|
+
headers?: unknown;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export function resolveOpenVikingRequestHeaders(
|
|
8
|
+
options: ResolveOpenVikingRequestHeadersOptions = {},
|
|
9
|
+
): OpenVikingRequestHeaders {
|
|
10
|
+
return cleanOpenVikingRequestHeaders(options.headers);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function cleanOpenVikingRequestHeaders(headers: unknown): OpenVikingRequestHeaders {
|
|
14
|
+
if (headers === undefined || headers === null) {
|
|
15
|
+
return {};
|
|
16
|
+
}
|
|
17
|
+
if (typeof headers !== "object" || Array.isArray(headers)) {
|
|
18
|
+
throw new Error("openviking request headers must be an object");
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const out: OpenVikingRequestHeaders = {};
|
|
22
|
+
for (const [key, value] of Object.entries(headers as Record<string, unknown>)) {
|
|
23
|
+
if (typeof value !== "string") {
|
|
24
|
+
throw new Error(`openviking request header ${key} must be a string`);
|
|
25
|
+
}
|
|
26
|
+
out[key] = value;
|
|
27
|
+
}
|
|
28
|
+
return out;
|
|
29
|
+
}
|
|
@@ -15,6 +15,47 @@ export type SessionAgentLookup = {
|
|
|
15
15
|
ovSessionId?: string;
|
|
16
16
|
};
|
|
17
17
|
|
|
18
|
+
export type OpenVikingPeerRole = "none" | "assistant" | "person";
|
|
19
|
+
|
|
20
|
+
export function sanitizeOpenVikingPeerId(raw?: string): string | undefined {
|
|
21
|
+
const normalized = raw
|
|
22
|
+
?.trim()
|
|
23
|
+
.replace(/[^a-zA-Z0-9_-]+/g, "_")
|
|
24
|
+
.replace(/^_+|_+$/g, "")
|
|
25
|
+
.replace(/_+/g, "_");
|
|
26
|
+
return normalized || undefined;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function resolveOpenVikingMessagePeerId(params: {
|
|
30
|
+
peerRole: OpenVikingPeerRole;
|
|
31
|
+
role?: string;
|
|
32
|
+
personPeerId?: string;
|
|
33
|
+
assistantPeerId?: string;
|
|
34
|
+
}): string | undefined {
|
|
35
|
+
if (params.peerRole === "person" && params.role === "user") {
|
|
36
|
+
return params.personPeerId;
|
|
37
|
+
}
|
|
38
|
+
if (params.peerRole === "assistant" && params.role === "assistant") {
|
|
39
|
+
return params.assistantPeerId;
|
|
40
|
+
}
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function resolveOpenVikingActorPeerId(params: {
|
|
45
|
+
peerRole: OpenVikingPeerRole;
|
|
46
|
+
personPeerId?: string;
|
|
47
|
+
assistantPeerId?: string;
|
|
48
|
+
}): string | undefined {
|
|
49
|
+
const actorPeerId = resolveOpenVikingMessagePeerId({
|
|
50
|
+
...params,
|
|
51
|
+
role: params.peerRole === "person" ? "user" : "assistant",
|
|
52
|
+
});
|
|
53
|
+
if (params.peerRole === "person" && !actorPeerId) {
|
|
54
|
+
throw new Error("openviking: peer_role=person requires a sender identity");
|
|
55
|
+
}
|
|
56
|
+
return actorPeerId;
|
|
57
|
+
}
|
|
58
|
+
|
|
18
59
|
export type SessionAgentResolveBranch =
|
|
19
60
|
| "session_resolved"
|
|
20
61
|
| "config_only_fallback"
|
package/routing/memory-uri.ts
CHANGED
|
@@ -3,7 +3,13 @@ import { DEFAULT_PHASE2_POLL_TIMEOUT_MS, type OpenVikingClient, type OVMessage }
|
|
|
3
3
|
import type { EffectiveQueryConfig } from "../query-config.js";
|
|
4
4
|
import { buildAutoRecallContext, prepareRecallQuery } from "../auto-recall.js";
|
|
5
5
|
import { toJsonLog } from "../memory-ranking.js";
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
openClawSessionToOvStorageId,
|
|
8
|
+
resolveOpenVikingActorPeerId,
|
|
9
|
+
resolveOpenVikingMessagePeerId,
|
|
10
|
+
sanitizeOpenVikingPeerId,
|
|
11
|
+
type OpenVikingPeerRole,
|
|
12
|
+
} from "../routing/identity-routing.js";
|
|
7
13
|
import { extractNewTurnMessages } from "../text-utils.js";
|
|
8
14
|
import { estimateAgentMessageTokens, estimateTextTokens } from "../token-estimator.js";
|
|
9
15
|
import {
|
|
@@ -26,7 +32,6 @@ export type CommitOpenVikingSessionParams = {
|
|
|
26
32
|
sessionKey?: string;
|
|
27
33
|
getClient: () => Promise<Pick<OpenVikingClient, "commitSession">>;
|
|
28
34
|
logger: ContextEngineLifecycleLogger;
|
|
29
|
-
resolveAgentId: (sessionId: string, sessionKey?: string, ovSessionId?: string) => string;
|
|
30
35
|
rememberSessionAgentId?: (ctx: {
|
|
31
36
|
agentId?: string;
|
|
32
37
|
sessionId?: string;
|
|
@@ -123,12 +128,15 @@ export type AfterTurnOpenVikingSessionParams = {
|
|
|
123
128
|
messages?: AgentMessage[];
|
|
124
129
|
prePromptMessageCount?: number;
|
|
125
130
|
isHeartbeat?: boolean;
|
|
131
|
+
/** Model context window in tokens; the auto-commit threshold is a fraction of this. */
|
|
132
|
+
tokenBudget: number;
|
|
126
133
|
runtimeContext?: Record<string, unknown>;
|
|
127
134
|
cfg: {
|
|
128
135
|
autoCapture: boolean;
|
|
129
|
-
|
|
136
|
+
commitTokenThresholdRatio: number;
|
|
130
137
|
commitKeepRecentCount: number;
|
|
131
138
|
logFindRequests: boolean;
|
|
139
|
+
peer_role?: OpenVikingPeerRole;
|
|
132
140
|
};
|
|
133
141
|
getClient: () => Promise<AfterTurnClient>;
|
|
134
142
|
logger: ContextEngineLifecycleLogger;
|
|
@@ -175,7 +183,6 @@ function sleep(ms: number): Promise<void> {
|
|
|
175
183
|
async function pollPhase2ExtractionOutcome(
|
|
176
184
|
client: AfterTurnClient,
|
|
177
185
|
taskId: string,
|
|
178
|
-
agentId: string,
|
|
179
186
|
logger: ContextEngineLifecycleLogger,
|
|
180
187
|
sessionLabel: string,
|
|
181
188
|
): Promise<void> {
|
|
@@ -183,7 +190,7 @@ async function pollPhase2ExtractionOutcome(
|
|
|
183
190
|
try {
|
|
184
191
|
while (Date.now() < deadline) {
|
|
185
192
|
await sleep(PHASE2_POLL_INTERVAL_MS);
|
|
186
|
-
const task = await client.getTask(taskId
|
|
193
|
+
const task = await client.getTask(taskId).catch((e) => {
|
|
187
194
|
logger.warn?.(`openviking: phase2 getTask failed task_id=${taskId}: ${String(e)}`);
|
|
188
195
|
return null;
|
|
189
196
|
});
|
|
@@ -338,7 +345,6 @@ export async function commitOpenVikingSession({
|
|
|
338
345
|
sessionKey,
|
|
339
346
|
getClient,
|
|
340
347
|
logger,
|
|
341
|
-
resolveAgentId,
|
|
342
348
|
rememberSessionAgentId,
|
|
343
349
|
isBypassedSession,
|
|
344
350
|
}: CommitOpenVikingSessionParams): Promise<boolean> {
|
|
@@ -356,10 +362,8 @@ export async function commitOpenVikingSession({
|
|
|
356
362
|
sessionKey,
|
|
357
363
|
ovSessionId: ovId,
|
|
358
364
|
});
|
|
359
|
-
const agentId = resolveAgentId(sessionId, sessionKey, ovId);
|
|
360
365
|
const commitResult = await client.commitSession(ovId, {
|
|
361
366
|
wait: true,
|
|
362
|
-
agentId,
|
|
363
367
|
keepRecentCount: 0,
|
|
364
368
|
});
|
|
365
369
|
const memCount = totalExtractedMemories(commitResult.memories_extracted);
|
|
@@ -404,6 +408,11 @@ function assemblePassthrough(
|
|
|
404
408
|
return { messages: liveMessages, estimatedTokens: originalTokens };
|
|
405
409
|
}
|
|
406
410
|
|
|
411
|
+
function isSessionNotFoundError(err: unknown): boolean {
|
|
412
|
+
const errorMessage = String(err);
|
|
413
|
+
return errorMessage.includes("[NOT_FOUND]") && errorMessage.includes("Session not found");
|
|
414
|
+
}
|
|
415
|
+
|
|
407
416
|
export async function assembleOpenVikingSession({
|
|
408
417
|
sessionId,
|
|
409
418
|
sessionKey,
|
|
@@ -485,6 +494,11 @@ export async function assembleOpenVikingSession({
|
|
|
485
494
|
const client = await getClient();
|
|
486
495
|
const routingRef = sessionId ?? sessionKey ?? ovSessionId;
|
|
487
496
|
const agentId = resolveAgentId(routingRef, sessionKey, ovSessionId);
|
|
497
|
+
const actorPeerId = resolveOpenVikingActorPeerId({
|
|
498
|
+
peerRole: cfg.peer_role ?? "assistant",
|
|
499
|
+
personPeerId: sanitizeOpenVikingPeerId(sender.senderId),
|
|
500
|
+
assistantPeerId: agentId,
|
|
501
|
+
});
|
|
488
502
|
const queryConfig = await queryConfigStore?.getEffective({
|
|
489
503
|
agentId,
|
|
490
504
|
sessionId,
|
|
@@ -496,6 +510,7 @@ export async function assembleOpenVikingSession({
|
|
|
496
510
|
queryConfig,
|
|
497
511
|
client,
|
|
498
512
|
agentId,
|
|
513
|
+
actorPeerId,
|
|
499
514
|
queryText: recallQuery.query,
|
|
500
515
|
logger,
|
|
501
516
|
verbose: (message) => logger.info(message),
|
|
@@ -546,9 +561,7 @@ export async function assembleOpenVikingSession({
|
|
|
546
561
|
|
|
547
562
|
try {
|
|
548
563
|
const client = await getClient();
|
|
549
|
-
const
|
|
550
|
-
const agentId = resolveAgentId(routingRef, sessionKey, ovSessionId);
|
|
551
|
-
const ctx = await client.getSessionContext(ovSessionId, tokenBudget, agentId);
|
|
564
|
+
const ctx = await client.getSessionContext(ovSessionId, tokenBudget);
|
|
552
565
|
|
|
553
566
|
const preAbstracts = ctx?.pre_archive_abstracts ?? [];
|
|
554
567
|
const hasArchives = !!ctx?.latest_archive_overview || preAbstracts.length > 0;
|
|
@@ -625,6 +638,27 @@ export async function assembleOpenVikingSession({
|
|
|
625
638
|
...(instruction.text ? { systemPromptAddition: instruction.text } : {}),
|
|
626
639
|
};
|
|
627
640
|
} catch (err) {
|
|
641
|
+
if (isSessionNotFoundError(err)) {
|
|
642
|
+
const errorMessage = String(err);
|
|
643
|
+
logger.info(
|
|
644
|
+
`openviking: assemble skipped because OV session does not exist ` +
|
|
645
|
+
`(session=${ovSessionId}, tokenBudget=${tokenBudget}, agentId=${resolveAgentId(ovSessionId)})`,
|
|
646
|
+
);
|
|
647
|
+
return assemblePassthrough({
|
|
648
|
+
diag,
|
|
649
|
+
ovSessionId,
|
|
650
|
+
reason: "session_not_found",
|
|
651
|
+
liveMessages: messages,
|
|
652
|
+
originalTokens,
|
|
653
|
+
extra: {
|
|
654
|
+
error: errorMessage,
|
|
655
|
+
tokenBudget,
|
|
656
|
+
agentId: resolveAgentId(ovSessionId),
|
|
657
|
+
senderIdFound: sender.found,
|
|
658
|
+
senderId: sender.senderId ?? null,
|
|
659
|
+
},
|
|
660
|
+
});
|
|
661
|
+
}
|
|
628
662
|
logger.warn?.(
|
|
629
663
|
`openviking: assemble failed for session=${ovSessionId}, ` +
|
|
630
664
|
`tokenBudget=${tokenBudget}, agentId=${resolveAgentId(ovSessionId)}: ${String(err)}`,
|
|
@@ -688,7 +722,7 @@ function extractRuntimeAgentId(runtimeContext: Record<string, unknown> | undefin
|
|
|
688
722
|
}
|
|
689
723
|
|
|
690
724
|
function isToolOnlyMessage(msg: ExtractedTurnMessage): boolean {
|
|
691
|
-
return msg.role === "
|
|
725
|
+
return msg.role === "assistant" && msg.parts.length > 0 && msg.parts.every((part) => part.type === "tool");
|
|
692
726
|
}
|
|
693
727
|
|
|
694
728
|
function coalesceConsecutiveToolMessages(messages: ExtractedTurnMessage[]): ExtractedTurnMessage[] {
|
|
@@ -705,7 +739,7 @@ function coalesceConsecutiveToolMessages(messages: ExtractedTurnMessage[]): Extr
|
|
|
705
739
|
for (const msg of messages) {
|
|
706
740
|
if (isToolOnlyMessage(msg)) {
|
|
707
741
|
if (!pendingTools) {
|
|
708
|
-
pendingTools = { role: "
|
|
742
|
+
pendingTools = { role: "assistant", parts: [] };
|
|
709
743
|
}
|
|
710
744
|
pendingTools.parts.push(...msg.parts);
|
|
711
745
|
continue;
|
|
@@ -753,6 +787,7 @@ export async function afterTurnOpenVikingSession({
|
|
|
753
787
|
messages: rawMessages,
|
|
754
788
|
prePromptMessageCount,
|
|
755
789
|
isHeartbeat,
|
|
790
|
+
tokenBudget,
|
|
756
791
|
runtimeContext,
|
|
757
792
|
cfg,
|
|
758
793
|
getClient,
|
|
@@ -870,21 +905,30 @@ export async function afterTurnOpenVikingSession({
|
|
|
870
905
|
ovSessionId,
|
|
871
906
|
msg.role,
|
|
872
907
|
ovParts,
|
|
873
|
-
|
|
908
|
+
undefined,
|
|
874
909
|
createdAt,
|
|
875
|
-
|
|
910
|
+
resolveOpenVikingMessagePeerId({
|
|
911
|
+
peerRole: cfg.peer_role ?? "assistant",
|
|
912
|
+
role: msg.role,
|
|
913
|
+
personPeerId: senderRoleId,
|
|
914
|
+
assistantPeerId: agentId,
|
|
915
|
+
}),
|
|
876
916
|
);
|
|
877
917
|
}
|
|
878
918
|
}
|
|
879
919
|
|
|
880
|
-
const session = await client.getSession(ovSessionId
|
|
920
|
+
const session = await client.getSession(ovSessionId);
|
|
881
921
|
const pendingTokens = session.pending_tokens ?? 0;
|
|
882
922
|
|
|
883
|
-
|
|
923
|
+
const commitTokenThreshold = Math.floor(tokenBudget * cfg.commitTokenThresholdRatio);
|
|
924
|
+
|
|
925
|
+
if (pendingTokens < commitTokenThreshold) {
|
|
884
926
|
diag("afterTurn_skip", ovSessionId, {
|
|
885
927
|
reason: "below_threshold",
|
|
886
928
|
pendingTokens,
|
|
887
|
-
commitTokenThreshold
|
|
929
|
+
commitTokenThreshold,
|
|
930
|
+
commitTokenThresholdRatio: cfg.commitTokenThresholdRatio,
|
|
931
|
+
tokenBudget,
|
|
888
932
|
senderIdFound: sender.found,
|
|
889
933
|
senderId: sender.senderId ?? null,
|
|
890
934
|
});
|
|
@@ -893,7 +937,6 @@ export async function afterTurnOpenVikingSession({
|
|
|
893
937
|
|
|
894
938
|
const commitResult = await client.commitSession(ovSessionId, {
|
|
895
939
|
wait: false,
|
|
896
|
-
agentId,
|
|
897
940
|
keepRecentCount: cfg.commitKeepRecentCount,
|
|
898
941
|
});
|
|
899
942
|
logger.info(
|
|
@@ -904,7 +947,9 @@ export async function afterTurnOpenVikingSession({
|
|
|
904
947
|
|
|
905
948
|
diag("afterTurn_commit", ovSessionId, {
|
|
906
949
|
pendingTokens,
|
|
907
|
-
commitTokenThreshold
|
|
950
|
+
commitTokenThreshold,
|
|
951
|
+
commitTokenThresholdRatio: cfg.commitTokenThresholdRatio,
|
|
952
|
+
tokenBudget,
|
|
908
953
|
status: commitResult.status,
|
|
909
954
|
archived: commitResult.archived ?? false,
|
|
910
955
|
taskId: commitResult.task_id ?? null,
|
|
@@ -917,7 +962,7 @@ export async function afterTurnOpenVikingSession({
|
|
|
917
962
|
`openviking: Phase2 memory extraction runs asynchronously on the server (task_id=${commitResult.task_id}). ` +
|
|
918
963
|
"memories_extracted appears only after that task completes — not in this immediate response.",
|
|
919
964
|
);
|
|
920
|
-
void pollPhase2ExtractionOutcome(client, commitResult.task_id,
|
|
965
|
+
void pollPhase2ExtractionOutcome(client, commitResult.task_id, logger, ovSessionId);
|
|
921
966
|
}
|
|
922
967
|
} catch (err) {
|
|
923
968
|
logger.warn?.(`openviking: afterTurn failed: ${String(err)}`);
|
|
@@ -998,7 +1043,7 @@ export async function compactOpenVikingSession({
|
|
|
998
1043
|
let preCommitEstimatedTokens: number | undefined;
|
|
999
1044
|
if (typeof tokensBeforeOriginal !== "number") {
|
|
1000
1045
|
try {
|
|
1001
|
-
const preCtx = await client.getSessionContext(ovSessionId, tokenBudget
|
|
1046
|
+
const preCtx = await client.getSessionContext(ovSessionId, tokenBudget);
|
|
1002
1047
|
if (typeof preCtx.estimatedTokens === "number" && Number.isFinite(preCtx.estimatedTokens)) {
|
|
1003
1048
|
preCommitEstimatedTokens = preCtx.estimatedTokens;
|
|
1004
1049
|
}
|
|
@@ -1018,7 +1063,6 @@ export async function compactOpenVikingSession({
|
|
|
1018
1063
|
);
|
|
1019
1064
|
const commitResult = await client.commitSession(ovSessionId, {
|
|
1020
1065
|
wait: true,
|
|
1021
|
-
agentId,
|
|
1022
1066
|
keepRecentCount: 0,
|
|
1023
1067
|
});
|
|
1024
1068
|
const memCount = totalExtractedMemories(commitResult.memories_extracted);
|
|
@@ -1094,7 +1138,7 @@ export async function compactOpenVikingSession({
|
|
|
1094
1138
|
let contextFetchError: string | undefined;
|
|
1095
1139
|
|
|
1096
1140
|
try {
|
|
1097
|
-
const ctx = await client.getSessionContext(ovSessionId, tokenBudget
|
|
1141
|
+
const ctx = await client.getSessionContext(ovSessionId, tokenBudget);
|
|
1098
1142
|
logger.info(
|
|
1099
1143
|
`openviking: compact getSessionContext raw result for ${ovSessionId}: ` +
|
|
1100
1144
|
JSON.stringify(ctx, null, 2),
|
|
@@ -1183,7 +1227,7 @@ export async function compactOpenVikingSession({
|
|
|
1183
1227
|
};
|
|
1184
1228
|
} catch (err) {
|
|
1185
1229
|
const errorMessage = String(err);
|
|
1186
|
-
if (
|
|
1230
|
+
if (isSessionNotFoundError(err)) {
|
|
1187
1231
|
logger.info(
|
|
1188
1232
|
`openviking: compact skipped because OV session does not exist ` +
|
|
1189
1233
|
`(session=${ovSessionId}, agentId=${agentId})`,
|