@openviking/openclaw-plugin 2026.6.2 → 2026.6.17-dev.2
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 +4 -4
- package/INSTALL-ZH.md +68 -8
- package/INSTALL.md +12 -8
- package/README.md +45 -35
- package/adapters/http-transport.ts +3 -0
- package/adapters/resource-packager.ts +141 -0
- package/auto-recall.ts +258 -27
- package/client.ts +201 -177
- package/commands/setup.ts +200 -33
- package/config/feature-gates.json +10 -0
- package/config.ts +428 -85
- package/context-engine.ts +71 -1402
- package/dist/adapters/http-transport.js +1 -0
- package/dist/adapters/resource-packager.js +114 -0
- package/dist/auto-recall.js +201 -25
- package/dist/client.js +125 -161
- package/dist/commands/setup.js +170 -32
- package/dist/config.js +285 -64
- package/dist/context-engine.js +62 -1173
- package/dist/index.js +209 -1489
- package/dist/memory-ranking.js +35 -12
- package/dist/plugin/command-registration.js +5 -0
- package/dist/plugin/openviking-archive-tools.js +210 -0
- package/dist/plugin/openviking-bypass-runtime.js +8 -0
- package/dist/plugin/openviking-client-runtime.js +28 -0
- package/dist/plugin/openviking-command-args.js +146 -0
- package/dist/plugin/openviking-command-definitions.js +206 -0
- package/dist/plugin/openviking-context-engine-ref.js +11 -0
- package/dist/plugin/openviking-context-engine-registration.js +23 -0
- package/dist/plugin/openviking-feature-gates.js +250 -0
- package/dist/plugin/openviking-import-runtime.js +50 -0
- package/dist/plugin/openviking-import-tools.js +86 -0
- package/dist/plugin/openviking-lifecycle-hooks.js +33 -0
- package/dist/plugin/openviking-memory-recall-tools.js +233 -0
- package/dist/plugin/openviking-memory-tools.js +199 -0
- package/dist/plugin/openviking-query-config-command.js +203 -0
- package/dist/plugin/openviking-query-runtime.js +367 -0
- package/dist/plugin/openviking-query-tools.js +91 -0
- package/dist/plugin/openviking-recall-trace-runtime.js +387 -0
- package/dist/plugin/openviking-recall-trace-tools.js +38 -0
- package/dist/plugin/openviking-runtime-state.js +26 -0
- package/dist/plugin/openviking-runtime-utils.js +62 -0
- package/dist/plugin/openviking-services.js +20 -0
- package/dist/plugin/openviking-session-routing-runtime.js +61 -0
- package/dist/plugin/openviking-tool-result-tools.js +268 -0
- package/dist/plugin/recall-trace-routes.js +77 -0
- package/dist/plugin/tool-registration.js +23 -0
- package/dist/process-manager.js +1 -1
- package/dist/query-config.js +336 -0
- package/dist/recall-trace.js +309 -0
- package/dist/registries/recall-resource-types.js +58 -0
- package/dist/routing/identity-routing.js +152 -0
- package/dist/routing/memory-uri.js +7 -0
- package/dist/services/context-lifecycle-service.js +847 -0
- package/dist/services/context-message-adapter.js +340 -0
- package/dist/session-transcript-repair.js +0 -1
- package/dist/text-utils.js +14 -31
- package/index.ts +271 -1873
- package/install-manifest.json +10 -1
- package/memory-ranking.ts +46 -10
- package/openclaw.plugin.json +198 -36
- package/package.json +14 -4
- package/plugin/command-registration.ts +34 -0
- package/plugin/openviking-archive-tools.ts +301 -0
- package/plugin/openviking-bypass-runtime.ts +26 -0
- package/plugin/openviking-client-runtime.ts +74 -0
- package/plugin/openviking-command-args.ts +187 -0
- package/plugin/openviking-command-definitions.ts +294 -0
- package/plugin/openviking-context-engine-ref.ts +16 -0
- package/plugin/openviking-context-engine-registration.ts +97 -0
- package/plugin/openviking-feature-gates.ts +335 -0
- package/plugin/openviking-import-runtime.ts +105 -0
- package/plugin/openviking-import-tools.ts +133 -0
- package/plugin/openviking-lifecycle-hooks.ts +65 -0
- package/plugin/openviking-memory-recall-tools.ts +368 -0
- package/plugin/openviking-memory-tools.ts +299 -0
- package/plugin/openviking-query-config-command.ts +230 -0
- package/plugin/openviking-query-formatters.ts +108 -0
- package/plugin/openviking-query-runtime.ts +476 -0
- package/plugin/openviking-query-tools.ts +161 -0
- package/plugin/openviking-recall-trace-runtime.ts +482 -0
- package/plugin/openviking-recall-trace-tools.ts +99 -0
- package/plugin/openviking-runtime-state.ts +52 -0
- package/plugin/openviking-runtime-utils.ts +67 -0
- package/plugin/openviking-services.ts +49 -0
- package/plugin/openviking-session-routing-runtime.ts +105 -0
- package/plugin/openviking-tool-result-tools.ts +397 -0
- package/plugin/recall-trace-routes.ts +133 -0
- package/plugin/tool-registration.ts +51 -0
- package/process-manager.ts +1 -1
- package/query-config.ts +400 -0
- package/recall-trace.ts +442 -0
- package/registries/openviking-tools.ts +84 -0
- package/registries/recall-resource-types.ts +74 -0
- package/routing/identity-routing.ts +210 -0
- package/routing/memory-uri.ts +8 -0
- package/services/context-lifecycle-service.ts +1209 -0
- package/services/context-message-adapter.ts +375 -0
- package/services/setup/config-writer.ts +125 -0
- package/services/setup/exit-utils.ts +5 -0
- package/services/setup/package-metadata.ts +56 -0
- package/services/setup/probe-service.ts +143 -0
- package/services/setup/setup-flow.ts +306 -0
- package/services/setup/version-compatibility.ts +41 -0
- package/session-transcript-repair.ts +0 -2
- package/skills/install-openviking-memory/SKILL.md +123 -50
- package/skills/openviking-context-database/SKILL.md +286 -0
- package/text-utils.ts +15 -34
package/index.ts
CHANGED
|
@@ -1,24 +1,47 @@
|
|
|
1
|
-
import { Type } from "@sinclair/typebox";
|
|
2
1
|
import { memoryOpenVikingConfigSchema } from "./config.js";
|
|
3
2
|
import { registerSetupCli } from "./commands/setup.js";
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
} from "./
|
|
16
|
-
import {
|
|
3
|
+
import { createOpenVikingBypassRuntime } from "./plugin/openviking-bypass-runtime.js";
|
|
4
|
+
import { createOpenVikingClientRuntime } from "./plugin/openviking-client-runtime.js";
|
|
5
|
+
import { createOpenVikingCommandDefinitions } from "./plugin/openviking-command-definitions.js";
|
|
6
|
+
import {
|
|
7
|
+
parseAddResourceCommandArgs,
|
|
8
|
+
parseAddSkillCommandArgs,
|
|
9
|
+
parseOVSearchCommandArgs,
|
|
10
|
+
} from "./plugin/openviking-command-args.js";
|
|
11
|
+
import { createOpenVikingContextEngineRef } from "./plugin/openviking-context-engine-ref.js";
|
|
12
|
+
import { registerOpenVikingContextEngine } from "./plugin/openviking-context-engine-registration.js";
|
|
13
|
+
import { registerOpenVikingFeatureGatesMethod } from "./plugin/openviking-feature-gates.js";
|
|
14
|
+
import { createOpenVikingQueryConfigCommandHandler } from "./plugin/openviking-query-config-command.js";
|
|
15
|
+
import { createOpenVikingQueryRuntime } from "./plugin/openviking-query-runtime.js";
|
|
16
|
+
import { createOpenVikingRecallTraceRuntime } from "./plugin/openviking-recall-trace-runtime.js";
|
|
17
|
+
import { createOpenVikingToolRegistrationRuntime } from "./plugin/tool-registration.js";
|
|
18
|
+
import { registerOpenVikingCommands } from "./plugin/command-registration.js";
|
|
19
|
+
import { registerRecallTraceRoutes as registerRecallTraceRouteAdapters } from "./plugin/recall-trace-routes.js";
|
|
20
|
+
import { createOpenVikingService } from "./plugin/openviking-services.js";
|
|
21
|
+
import { registerOpenVikingArchiveTools } from "./plugin/openviking-archive-tools.js";
|
|
22
|
+
import { createOpenVikingImportRuntime } from "./plugin/openviking-import-runtime.js";
|
|
23
|
+
import { registerOpenVikingImportTools } from "./plugin/openviking-import-tools.js";
|
|
24
|
+
import { registerOpenVikingLifecycleHooks } from "./plugin/openviking-lifecycle-hooks.js";
|
|
25
|
+
import { registerOpenVikingMemoryRecallTools } from "./plugin/openviking-memory-recall-tools.js";
|
|
26
|
+
import { registerOpenVikingMemoryTools } from "./plugin/openviking-memory-tools.js";
|
|
27
|
+
import { registerOpenVikingQueryTools } from "./plugin/openviking-query-tools.js";
|
|
28
|
+
import { registerOpenVikingRecallTraceTools } from "./plugin/openviking-recall-trace-tools.js";
|
|
29
|
+
import { createOpenVikingRuntimeState } from "./plugin/openviking-runtime-state.js";
|
|
30
|
+
import { createOpenVikingSessionRoutingRuntime } from "./plugin/openviking-session-routing-runtime.js";
|
|
31
|
+
import { registerOpenVikingToolResultTools } from "./plugin/openviking-tool-result-tools.js";
|
|
17
32
|
import {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
33
|
+
boundTraceQuery,
|
|
34
|
+
createMemoryStoreTempSessionId,
|
|
35
|
+
createTraceId,
|
|
36
|
+
extractToolSenderId,
|
|
37
|
+
inferRecallResourceType,
|
|
38
|
+
makeBypassedToolResult,
|
|
39
|
+
previewText,
|
|
40
|
+
} from "./plugin/openviking-runtime-utils.js";
|
|
41
|
+
import type { CommandDefinition } from "./plugin/command-registration.js";
|
|
42
|
+
|
|
43
|
+
import type { HttpTransport } from "./adapters/http-transport.js";
|
|
44
|
+
import { formatMessageFaithful, toRoleId } from "./services/context-message-adapter.js";
|
|
22
45
|
import {
|
|
23
46
|
clampScore,
|
|
24
47
|
postProcessMemories,
|
|
@@ -27,33 +50,21 @@ import {
|
|
|
27
50
|
import { withTimeout } from "./process-manager.js";
|
|
28
51
|
import {
|
|
29
52
|
createMemoryOpenVikingContextEngine,
|
|
30
|
-
openClawSessionToOvStorageId,
|
|
31
|
-
openClawSessionRefToOvStorageId,
|
|
32
53
|
} from "./context-engine.js";
|
|
33
|
-
import type { ContextEngineWithCommit } from "./context-engine.js";
|
|
34
54
|
import {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
} from "./auto-recall.js";
|
|
40
|
-
export {
|
|
41
|
-
buildMemoryLines,
|
|
55
|
+
openClawSessionRefToOvStorageId,
|
|
56
|
+
openClawSessionToOvStorageId,
|
|
57
|
+
} from "./routing/identity-routing.js";
|
|
58
|
+
import {
|
|
42
59
|
buildMemoryLinesWithBudget,
|
|
43
|
-
estimateTokenCount,
|
|
44
|
-
prepareRecallQuery,
|
|
45
|
-
};
|
|
46
|
-
export {
|
|
47
|
-
estimateAgentMessageTokens,
|
|
48
|
-
estimateAgentMessagesTokens,
|
|
49
|
-
estimateSerializedTokens,
|
|
50
|
-
estimateTextTokens,
|
|
51
|
-
} from "./token-estimator.js";
|
|
52
|
-
export type {
|
|
53
|
-
BuildMemoryLinesOptions,
|
|
54
|
-
BuildMemoryLinesWithBudgetOptions,
|
|
55
|
-
PreparedRecallQuery,
|
|
56
60
|
} from "./auto-recall.js";
|
|
61
|
+
import {
|
|
62
|
+
normalizeRecallResourceTypes as normalizeResourceTypes,
|
|
63
|
+
resolveRecallSearchPlan,
|
|
64
|
+
} from "./registries/recall-resource-types.js";
|
|
65
|
+
import {
|
|
66
|
+
normalizeRuntimeQueryParams,
|
|
67
|
+
} from "./query-config.js";
|
|
57
68
|
|
|
58
69
|
type PluginLogger = {
|
|
59
70
|
debug?: (message: string) => void;
|
|
@@ -68,34 +79,6 @@ type HookAgentContext = {
|
|
|
68
79
|
sessionKey?: string;
|
|
69
80
|
};
|
|
70
81
|
|
|
71
|
-
type SessionAgentLookup = {
|
|
72
|
-
agentId?: string;
|
|
73
|
-
sessionId?: string;
|
|
74
|
-
sessionKey?: string;
|
|
75
|
-
ovSessionId?: string;
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
type PluginSessionRouting = {
|
|
79
|
-
sessionId?: string;
|
|
80
|
-
sessionKey?: string;
|
|
81
|
-
ovSessionId?: string;
|
|
82
|
-
agentId: string;
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
type SessionAgentResolveBranch =
|
|
86
|
-
| "session_resolved"
|
|
87
|
-
| "config_only_fallback"
|
|
88
|
-
| "default_no_session";
|
|
89
|
-
|
|
90
|
-
export type SessionAgentResolveResult = {
|
|
91
|
-
resolved: string;
|
|
92
|
-
resolvedBeforeSanitize: string;
|
|
93
|
-
branch: SessionAgentResolveBranch;
|
|
94
|
-
mappedResolvedAgentId: string | null;
|
|
95
|
-
aliases: string[];
|
|
96
|
-
fromExplicitBinding: boolean;
|
|
97
|
-
};
|
|
98
|
-
|
|
99
82
|
type ToolDefinition = {
|
|
100
83
|
name: string;
|
|
101
84
|
label: string;
|
|
@@ -111,91 +94,9 @@ type ToolContext = {
|
|
|
111
94
|
senderId?: string;
|
|
112
95
|
};
|
|
113
96
|
|
|
114
|
-
type PluginCommandContext = {
|
|
115
|
-
args?: string;
|
|
116
|
-
commandBody: string;
|
|
117
|
-
sessionKey?: string;
|
|
118
|
-
sessionId?: string;
|
|
119
|
-
agentId?: string;
|
|
120
|
-
ovSessionId?: string;
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
type CommandResult = {
|
|
124
|
-
text: string;
|
|
125
|
-
details?: Record<string, unknown>;
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
type CommandDefinition = {
|
|
129
|
-
name: string;
|
|
130
|
-
description: string;
|
|
131
|
-
acceptsArgs?: boolean;
|
|
132
|
-
requireAuth?: boolean;
|
|
133
|
-
handler: (ctx: PluginCommandContext) => CommandResult | Promise<CommandResult>;
|
|
134
|
-
};
|
|
135
|
-
|
|
136
|
-
type AddResourceToolInput = {
|
|
137
|
-
source?: string;
|
|
138
|
-
to?: string;
|
|
139
|
-
parent?: string;
|
|
140
|
-
reason?: string;
|
|
141
|
-
instruction?: string;
|
|
142
|
-
wait?: boolean;
|
|
143
|
-
timeout?: number;
|
|
144
|
-
};
|
|
145
|
-
|
|
146
|
-
type AddSkillToolInput = {
|
|
147
|
-
source?: string;
|
|
148
|
-
data?: unknown;
|
|
149
|
-
wait?: boolean;
|
|
150
|
-
timeout?: number;
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
type OVSearchInput = {
|
|
154
|
-
query: string;
|
|
155
|
-
uri?: string;
|
|
156
|
-
limit?: number;
|
|
157
|
-
};
|
|
158
|
-
|
|
159
|
-
type ToolResultRef = {
|
|
160
|
-
sessionId: string;
|
|
161
|
-
toolResultId: string;
|
|
162
|
-
ref: string;
|
|
163
|
-
};
|
|
164
|
-
|
|
165
|
-
function parseToolResultRef(value: unknown): ToolResultRef | null {
|
|
166
|
-
const raw = typeof value === "string" ? value.trim() : "";
|
|
167
|
-
if (!raw) {
|
|
168
|
-
return null;
|
|
169
|
-
}
|
|
170
|
-
const match = raw.match(/^viking:\/\/session\/([^/]+)\/tool-results\/([^/?#]+)(?:[?#].*)?$/);
|
|
171
|
-
if (!match) {
|
|
172
|
-
return null;
|
|
173
|
-
}
|
|
174
|
-
const sessionId = decodeURIComponent(match[1]!);
|
|
175
|
-
const toolResultId = decodeURIComponent(match[2]!);
|
|
176
|
-
if (!sessionId || !toolResultId) {
|
|
177
|
-
return null;
|
|
178
|
-
}
|
|
179
|
-
return {
|
|
180
|
-
sessionId,
|
|
181
|
-
toolResultId,
|
|
182
|
-
ref: `viking://session/${encodeURIComponent(sessionId)}/tool-results/${encodeURIComponent(toolResultId)}`,
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
function getOptionalInteger(value: unknown, fallback: number): number {
|
|
187
|
-
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
188
|
-
return fallback;
|
|
189
|
-
}
|
|
190
|
-
return Math.floor(value);
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
function getPositiveInteger(value: unknown, fallback: number): number {
|
|
194
|
-
return Math.max(1, getOptionalInteger(value, fallback));
|
|
195
|
-
}
|
|
196
|
-
|
|
197
97
|
type OpenClawPluginApi = {
|
|
198
98
|
pluginConfig?: unknown;
|
|
99
|
+
openVikingTransport?: HttpTransport;
|
|
199
100
|
logger: PluginLogger;
|
|
200
101
|
registerTool: {
|
|
201
102
|
(tool: ToolDefinition, opts?: { name?: string; names?: string[] }): void;
|
|
@@ -211,6 +112,23 @@ type OpenClawPluginApi = {
|
|
|
211
112
|
stop?: (ctx?: unknown) => void | Promise<void>;
|
|
212
113
|
}) => void;
|
|
213
114
|
registerContextEngine?: (id: string, factory: () => unknown) => void;
|
|
115
|
+
registerGatewayMethod?: (
|
|
116
|
+
name: string,
|
|
117
|
+
handler: (input: {
|
|
118
|
+
params?: unknown;
|
|
119
|
+
respond: (success: boolean, data: unknown) => void;
|
|
120
|
+
}) => void | Promise<void>,
|
|
121
|
+
) => void;
|
|
122
|
+
registerHttpRoute?: (route: {
|
|
123
|
+
path: string;
|
|
124
|
+
auth: "gateway" | "plugin";
|
|
125
|
+
match?: "exact" | "prefix";
|
|
126
|
+
handler: (req: { method?: string; url?: string }, res: {
|
|
127
|
+
statusCode?: number;
|
|
128
|
+
setHeader?: (name: string, value: string) => void;
|
|
129
|
+
end?: (body?: string) => void;
|
|
130
|
+
}) => Promise<boolean> | boolean;
|
|
131
|
+
}) => void;
|
|
214
132
|
registerCli?: (
|
|
215
133
|
factory: (ctx: { program: unknown; workspaceDir?: string }) => void,
|
|
216
134
|
opts?: { commands?: string[] },
|
|
@@ -222,338 +140,15 @@ type OpenClawPluginApi = {
|
|
|
222
140
|
) => void;
|
|
223
141
|
};
|
|
224
142
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
export function sanitizeOpenVikingAgentIdHeader(raw: string): string {
|
|
233
|
-
const trimmed = raw.trim();
|
|
234
|
-
if (!trimmed) {
|
|
235
|
-
return "default";
|
|
236
|
-
}
|
|
237
|
-
const normalized = trimmed
|
|
238
|
-
.replace(/[^a-zA-Z0-9_-]/g, "_")
|
|
239
|
-
.replace(/_+/g, "_")
|
|
240
|
-
.replace(/^_|_$/g, "");
|
|
241
|
-
return normalized.length > 0 ? normalized : "ov_agent";
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
export function tokenizeCommandArgs(args: string): string[] {
|
|
245
|
-
const tokens: string[] = [];
|
|
246
|
-
let current = "";
|
|
247
|
-
let quote: "'" | '"' | null = null;
|
|
248
|
-
let escaping = false;
|
|
249
|
-
|
|
250
|
-
for (let i = 0; i < args.length; i += 1) {
|
|
251
|
-
const ch = args[i]!;
|
|
252
|
-
const next = args[i + 1];
|
|
253
|
-
if (escaping) {
|
|
254
|
-
current += ch;
|
|
255
|
-
escaping = false;
|
|
256
|
-
continue;
|
|
257
|
-
}
|
|
258
|
-
if (ch === "\\") {
|
|
259
|
-
const shouldEscape =
|
|
260
|
-
quote === '"'
|
|
261
|
-
? next === '"' || next === "\\"
|
|
262
|
-
: !quote && Boolean(next && (/\s/.test(next) || next === '"' || next === "'"));
|
|
263
|
-
if (shouldEscape) {
|
|
264
|
-
escaping = true;
|
|
265
|
-
continue;
|
|
266
|
-
}
|
|
267
|
-
current += ch;
|
|
268
|
-
continue;
|
|
269
|
-
}
|
|
270
|
-
if ((ch === '"' || ch === "'") && (!quote || quote === ch)) {
|
|
271
|
-
quote = quote ? null : ch;
|
|
272
|
-
continue;
|
|
273
|
-
}
|
|
274
|
-
if (!quote && /\s/.test(ch)) {
|
|
275
|
-
if (current) {
|
|
276
|
-
tokens.push(current);
|
|
277
|
-
current = "";
|
|
278
|
-
}
|
|
279
|
-
continue;
|
|
280
|
-
}
|
|
281
|
-
current += ch;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
if (escaping) {
|
|
285
|
-
current += "\\";
|
|
286
|
-
}
|
|
287
|
-
if (quote) {
|
|
288
|
-
throw new Error("Unterminated quoted argument");
|
|
289
|
-
}
|
|
290
|
-
if (current) {
|
|
291
|
-
tokens.push(current);
|
|
292
|
-
}
|
|
293
|
-
return tokens;
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
type ParsedFlagArgs = {
|
|
297
|
-
positionals: string[];
|
|
298
|
-
flags: Map<string, string | boolean>;
|
|
143
|
+
type RecallTraceRouteAdapter = {
|
|
144
|
+
registerRoute?: (route: {
|
|
145
|
+
method: "GET";
|
|
146
|
+
path: string;
|
|
147
|
+
handler: (request?: { query?: Record<string, unknown>; params?: Record<string, unknown>; url?: string }) => Promise<unknown>;
|
|
148
|
+
}) => void;
|
|
149
|
+
registerHttpRoute?: OpenClawPluginApi["registerHttpRoute"];
|
|
299
150
|
};
|
|
300
151
|
|
|
301
|
-
function parseFlagArgs(args: string): ParsedFlagArgs {
|
|
302
|
-
const tokens = tokenizeCommandArgs(args);
|
|
303
|
-
const positionals: string[] = [];
|
|
304
|
-
const flags = new Map<string, string | boolean>();
|
|
305
|
-
|
|
306
|
-
for (let i = 0; i < tokens.length; i += 1) {
|
|
307
|
-
const token = tokens[i]!;
|
|
308
|
-
if (!token.startsWith("--")) {
|
|
309
|
-
positionals.push(token);
|
|
310
|
-
continue;
|
|
311
|
-
}
|
|
312
|
-
const raw = token.slice(2);
|
|
313
|
-
if (!raw) {
|
|
314
|
-
continue;
|
|
315
|
-
}
|
|
316
|
-
const eqIndex = raw.indexOf("=");
|
|
317
|
-
if (eqIndex >= 0) {
|
|
318
|
-
flags.set(raw.slice(0, eqIndex), raw.slice(eqIndex + 1));
|
|
319
|
-
continue;
|
|
320
|
-
}
|
|
321
|
-
const next = tokens[i + 1];
|
|
322
|
-
if (next && !next.startsWith("--")) {
|
|
323
|
-
flags.set(raw, next);
|
|
324
|
-
i += 1;
|
|
325
|
-
} else {
|
|
326
|
-
flags.set(raw, true);
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
return { positionals, flags };
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
function getStringFlag(flags: Map<string, string | boolean>, name: string): string | undefined {
|
|
334
|
-
const value = flags.get(name);
|
|
335
|
-
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
function getNumberFlag(flags: Map<string, string | boolean>, name: string): number | undefined {
|
|
339
|
-
const raw = getStringFlag(flags, name);
|
|
340
|
-
if (!raw) {
|
|
341
|
-
return undefined;
|
|
342
|
-
}
|
|
343
|
-
const value = Number(raw);
|
|
344
|
-
if (!Number.isFinite(value)) {
|
|
345
|
-
throw new Error(`--${name} must be a number`);
|
|
346
|
-
}
|
|
347
|
-
return value;
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
function getBoolFlag(flags: Map<string, string | boolean>, name: string): boolean {
|
|
351
|
-
return flags.get(name) === true;
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
function extractToolSenderId(ctx: unknown): string | undefined {
|
|
355
|
-
if (!ctx || typeof ctx !== "object") {
|
|
356
|
-
return undefined;
|
|
357
|
-
}
|
|
358
|
-
const toolCtx = ctx as Record<string, unknown>;
|
|
359
|
-
if (typeof toolCtx.requesterSenderId === "string") {
|
|
360
|
-
const trimmed = toolCtx.requesterSenderId.trim();
|
|
361
|
-
if (trimmed) {
|
|
362
|
-
return trimmed;
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
if (typeof toolCtx.senderId === "string") {
|
|
366
|
-
const trimmed = toolCtx.senderId.trim();
|
|
367
|
-
if (trimmed) {
|
|
368
|
-
return trimmed;
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
return undefined;
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
export function parseAddResourceCommandArgs(args: string): AddResourceToolInput {
|
|
375
|
-
const parsed = parseFlagArgs(args);
|
|
376
|
-
const source =
|
|
377
|
-
parsed.positionals.length <= 1 ? parsed.positionals[0] : parsed.positionals.join(" ").trim();
|
|
378
|
-
if (!source) {
|
|
379
|
-
throw new Error("Usage: /add-resource <source> [--to URI] [--parent URI] [--reason TEXT] [--instruction TEXT] [--wait] [--timeout SEC]");
|
|
380
|
-
}
|
|
381
|
-
const to = getStringFlag(parsed.flags, "to");
|
|
382
|
-
const parent = getStringFlag(parsed.flags, "parent");
|
|
383
|
-
if (to && parent) {
|
|
384
|
-
throw new Error("Cannot specify both --to and --parent.");
|
|
385
|
-
}
|
|
386
|
-
return {
|
|
387
|
-
source,
|
|
388
|
-
to,
|
|
389
|
-
parent,
|
|
390
|
-
reason: getStringFlag(parsed.flags, "reason"),
|
|
391
|
-
instruction: getStringFlag(parsed.flags, "instruction"),
|
|
392
|
-
wait: getBoolFlag(parsed.flags, "wait"),
|
|
393
|
-
timeout: getNumberFlag(parsed.flags, "timeout"),
|
|
394
|
-
};
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
export function parseAddSkillCommandArgs(args: string): AddSkillToolInput {
|
|
398
|
-
const parsed = parseFlagArgs(args);
|
|
399
|
-
const source =
|
|
400
|
-
parsed.positionals.length <= 1 ? parsed.positionals[0] : parsed.positionals.join(" ").trim();
|
|
401
|
-
if (!source) {
|
|
402
|
-
throw new Error("Usage: /add-skill <source> [--wait] [--timeout SEC]");
|
|
403
|
-
}
|
|
404
|
-
if (parsed.flags.has("to") || parsed.flags.has("parent") || parsed.flags.has("reason") || parsed.flags.has("instruction")) {
|
|
405
|
-
throw new Error("--to, --parent, --reason, and --instruction are resource-only options.");
|
|
406
|
-
}
|
|
407
|
-
return {
|
|
408
|
-
source,
|
|
409
|
-
wait: getBoolFlag(parsed.flags, "wait"),
|
|
410
|
-
timeout: getNumberFlag(parsed.flags, "timeout"),
|
|
411
|
-
};
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
export function parseOVSearchCommandArgs(args: string): OVSearchInput {
|
|
415
|
-
const parsed = parseFlagArgs(args);
|
|
416
|
-
// `/ov-search` only accepts a single query string, so positional segments are
|
|
417
|
-
// always re-joined to preserve unquoted multi-word searches.
|
|
418
|
-
const query = parsed.positionals.join(" ").trim();
|
|
419
|
-
if (!query) {
|
|
420
|
-
throw new Error('Usage: /ov-search "<query>" [--uri URI] [--limit N]');
|
|
421
|
-
}
|
|
422
|
-
return {
|
|
423
|
-
query,
|
|
424
|
-
uri: getStringFlag(parsed.flags, "uri"),
|
|
425
|
-
limit: getNumberFlag(parsed.flags, "limit"),
|
|
426
|
-
};
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
function extractAgentIdFromSessionKey(sessionKey?: string): string | undefined {
|
|
430
|
-
const raw = typeof sessionKey === "string" ? sessionKey.trim() : "";
|
|
431
|
-
if (!raw) {
|
|
432
|
-
return undefined;
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
const match = raw.match(/^agent:([^:]+):/);
|
|
436
|
-
const agentId = match?.[1]?.trim();
|
|
437
|
-
return agentId || undefined;
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
function collectSessionAgentAliases(
|
|
441
|
-
sessionId?: string,
|
|
442
|
-
sessionKey?: string,
|
|
443
|
-
ovSessionId?: string,
|
|
444
|
-
): string[] {
|
|
445
|
-
const aliases = new Set<string>();
|
|
446
|
-
const sid = typeof sessionId === "string" ? sessionId.trim() : "";
|
|
447
|
-
const sk = typeof sessionKey === "string" ? sessionKey.trim() : "";
|
|
448
|
-
const ovSid = typeof ovSessionId === "string" ? ovSessionId.trim() : "";
|
|
449
|
-
|
|
450
|
-
if (sid) {
|
|
451
|
-
aliases.add(sid);
|
|
452
|
-
}
|
|
453
|
-
if (sk) {
|
|
454
|
-
aliases.add(sk);
|
|
455
|
-
}
|
|
456
|
-
if (ovSid) {
|
|
457
|
-
aliases.add(ovSid);
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
if (!ovSid && (sid || sk)) {
|
|
461
|
-
try {
|
|
462
|
-
aliases.add(
|
|
463
|
-
openClawSessionToOvStorageId(
|
|
464
|
-
sid || undefined,
|
|
465
|
-
sk || undefined,
|
|
466
|
-
),
|
|
467
|
-
);
|
|
468
|
-
} catch {
|
|
469
|
-
/* need a resolvable OpenClaw session identity */
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
return [...aliases];
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
export function createSessionAgentResolver(configAgentId: string) {
|
|
477
|
-
const configAgentPrefix = configAgentId.trim() === "default" ? "" : configAgentId.trim();
|
|
478
|
-
const sessionAgentIds = new Map<string, string>();
|
|
479
|
-
|
|
480
|
-
const remember = (ctx: SessionAgentLookup): void => {
|
|
481
|
-
const sessionScopedAgentId =
|
|
482
|
-
extractAgentIdFromSessionKey(ctx.sessionKey) ||
|
|
483
|
-
extractAgentIdFromSessionKey(ctx.sessionId);
|
|
484
|
-
const rawAgentId =
|
|
485
|
-
(typeof ctx.agentId === "string" ? ctx.agentId.trim() : "") ||
|
|
486
|
-
sessionScopedAgentId ||
|
|
487
|
-
"";
|
|
488
|
-
if (!rawAgentId) {
|
|
489
|
-
return;
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
const prefix = configAgentPrefix;
|
|
493
|
-
const resolvedBeforeSanitize = prefix ? `${prefix}_${rawAgentId}` : rawAgentId;
|
|
494
|
-
const resolved = sanitizeOpenVikingAgentIdHeader(resolvedBeforeSanitize);
|
|
495
|
-
for (const alias of collectSessionAgentAliases(ctx.sessionId, ctx.sessionKey, ctx.ovSessionId)) {
|
|
496
|
-
sessionAgentIds.set(alias, resolved);
|
|
497
|
-
}
|
|
498
|
-
};
|
|
499
|
-
|
|
500
|
-
const resolve = (
|
|
501
|
-
sessionId?: string,
|
|
502
|
-
sessionKey?: string,
|
|
503
|
-
ovSessionId?: string,
|
|
504
|
-
): SessionAgentResolveResult => {
|
|
505
|
-
const aliases = collectSessionAgentAliases(sessionId, sessionKey, ovSessionId);
|
|
506
|
-
const mappedAlias = aliases.find((alias) => sessionAgentIds.has(alias));
|
|
507
|
-
const mappedResolvedAgentId = mappedAlias ? sessionAgentIds.get(mappedAlias) : undefined;
|
|
508
|
-
const sessionScopedAgentId =
|
|
509
|
-
extractAgentIdFromSessionKey(sessionKey) ||
|
|
510
|
-
extractAgentIdFromSessionKey(sessionId);
|
|
511
|
-
|
|
512
|
-
let resolvedBeforeSanitize: string;
|
|
513
|
-
let resolved: string;
|
|
514
|
-
let branch: SessionAgentResolveBranch;
|
|
515
|
-
const prefix = configAgentPrefix;
|
|
516
|
-
|
|
517
|
-
if (mappedResolvedAgentId) {
|
|
518
|
-
resolvedBeforeSanitize = mappedResolvedAgentId;
|
|
519
|
-
resolved = mappedResolvedAgentId;
|
|
520
|
-
branch = "session_resolved";
|
|
521
|
-
} else if (sessionScopedAgentId) {
|
|
522
|
-
resolvedBeforeSanitize = prefix ? `${prefix}_${sessionScopedAgentId}` : sessionScopedAgentId;
|
|
523
|
-
resolved = sanitizeOpenVikingAgentIdHeader(resolvedBeforeSanitize);
|
|
524
|
-
branch = "session_resolved";
|
|
525
|
-
} else if (!prefix) {
|
|
526
|
-
resolvedBeforeSanitize = DEFAULT_OPENCLAW_AGENT_ID;
|
|
527
|
-
resolved = DEFAULT_OPENCLAW_AGENT_ID;
|
|
528
|
-
branch = "default_no_session";
|
|
529
|
-
} else {
|
|
530
|
-
resolvedBeforeSanitize = `${prefix}_${DEFAULT_OPENCLAW_AGENT_ID}`;
|
|
531
|
-
resolved = sanitizeOpenVikingAgentIdHeader(resolvedBeforeSanitize);
|
|
532
|
-
branch = "config_only_fallback";
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
return {
|
|
536
|
-
resolved,
|
|
537
|
-
resolvedBeforeSanitize,
|
|
538
|
-
branch,
|
|
539
|
-
mappedResolvedAgentId: mappedResolvedAgentId ?? null,
|
|
540
|
-
aliases,
|
|
541
|
-
fromExplicitBinding: !!(mappedResolvedAgentId || sessionScopedAgentId),
|
|
542
|
-
};
|
|
543
|
-
};
|
|
544
|
-
|
|
545
|
-
return {
|
|
546
|
-
remember,
|
|
547
|
-
resolve,
|
|
548
|
-
};
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
function totalCommitMemories(r: CommitSessionResult): number {
|
|
552
|
-
const m = r.memories_extracted;
|
|
553
|
-
if (!m || typeof m !== "object") return 0;
|
|
554
|
-
return Object.values(m).reduce((sum, n) => sum + (n ?? 0), 0);
|
|
555
|
-
}
|
|
556
|
-
|
|
557
152
|
const contextEnginePlugin = {
|
|
558
153
|
id: "openviking",
|
|
559
154
|
name: "Context Engine (OpenViking)",
|
|
@@ -562,6 +157,8 @@ const contextEnginePlugin = {
|
|
|
562
157
|
configSchema: memoryOpenVikingConfigSchema,
|
|
563
158
|
|
|
564
159
|
register(api: OpenClawPluginApi) {
|
|
160
|
+
registerOpenVikingFeatureGatesMethod(api);
|
|
161
|
+
|
|
565
162
|
const rawCfg =
|
|
566
163
|
api.pluginConfig && typeof api.pluginConfig === "object" && !Array.isArray(api.pluginConfig)
|
|
567
164
|
? (api.pluginConfig as Record<string, unknown>)
|
|
@@ -591,1419 +188,220 @@ const contextEnginePlugin = {
|
|
|
591
188
|
return;
|
|
592
189
|
}
|
|
593
190
|
|
|
594
|
-
const
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
const
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
};
|
|
606
|
-
verboseRoutingInfo(
|
|
607
|
-
`openviking: loaded plugin config agent_prefix="${cfg.agent_prefix}" ` +
|
|
608
|
-
`(raw plugins.entries.openviking.config.agent_prefix=${JSON.stringify(rawAgentId ?? "(missing)")}; ` +
|
|
609
|
-
`${
|
|
610
|
-
cfg.agent_prefix
|
|
611
|
-
? 'non-empty → X-OpenViking-Agent is <agent_prefix>_<ctx.agentId> when hooks expose session agent, or <agent_prefix>_main when ctx.agentId is unknown'
|
|
612
|
-
: 'empty → X-OpenViking-Agent follows OpenClaw ctx.agentId per session, or "main" when ctx.agentId is unknown'
|
|
613
|
-
})`,
|
|
614
|
-
);
|
|
615
|
-
verboseRoutingInfo(
|
|
616
|
-
`openviking: auth/namespace config ` +
|
|
617
|
-
JSON.stringify({
|
|
618
|
-
isolateUserScopeByAgent: cfg.isolateUserScopeByAgent,
|
|
619
|
-
isolateAgentScopeByUser: cfg.isolateAgentScopeByUser,
|
|
620
|
-
deprecatedAgentScopeMode: cfg.agentScopeMode,
|
|
621
|
-
}),
|
|
622
|
-
);
|
|
623
|
-
const routingDebugLog = cfg.logFindRequests
|
|
624
|
-
? (msg: string) => {
|
|
625
|
-
api.logger.info(msg);
|
|
626
|
-
}
|
|
627
|
-
: undefined;
|
|
628
|
-
const tenantAccount = cfg.accountId;
|
|
629
|
-
const tenantUser = cfg.userId;
|
|
630
|
-
|
|
631
|
-
const clientPromise = Promise.resolve(
|
|
632
|
-
new OpenVikingClient(
|
|
633
|
-
cfg.baseUrl,
|
|
634
|
-
cfg.apiKey,
|
|
635
|
-
cfg.agent_prefix,
|
|
636
|
-
cfg.timeoutMs,
|
|
637
|
-
tenantAccount,
|
|
638
|
-
tenantUser,
|
|
639
|
-
routingDebugLog,
|
|
640
|
-
cfg.isolateUserScopeByAgent,
|
|
641
|
-
cfg.isolateAgentScopeByUser,
|
|
642
|
-
),
|
|
643
|
-
);
|
|
644
|
-
|
|
645
|
-
const getClient = (): Promise<OpenVikingClient> => clientPromise;
|
|
646
|
-
|
|
647
|
-
const isBypassedSession = (ctx?: {
|
|
648
|
-
sessionId?: string;
|
|
649
|
-
sessionKey?: string;
|
|
650
|
-
}): boolean => shouldBypassSession(ctx ?? {}, bypassSessionPatterns);
|
|
651
|
-
|
|
652
|
-
const makeBypassedToolResult = (toolName: string) => ({
|
|
653
|
-
content: [
|
|
654
|
-
{
|
|
655
|
-
type: "text" as const,
|
|
656
|
-
text: `OpenViking is bypassed for this session by bypassSessionPatterns; ${toolName} was skipped.`,
|
|
657
|
-
},
|
|
658
|
-
],
|
|
659
|
-
details: {
|
|
660
|
-
action: "bypassed",
|
|
661
|
-
reason: "session_bypassed",
|
|
662
|
-
toolName,
|
|
663
|
-
},
|
|
191
|
+
const { isBypassedSession } = createOpenVikingBypassRuntime({ cfg });
|
|
192
|
+
const { getClient, verboseRoutingInfo } = createOpenVikingClientRuntime({
|
|
193
|
+
cfg,
|
|
194
|
+
rawPeerPrefix: rawCfg.peer_prefix,
|
|
195
|
+
logger: api.logger,
|
|
196
|
+
transport: api.openVikingTransport,
|
|
197
|
+
});
|
|
198
|
+
const { registerOpenVikingTool } = createOpenVikingToolRegistrationRuntime({
|
|
199
|
+
api: api as { registerTool: (toolOrFactory: unknown, opts: { name: string }) => void },
|
|
200
|
+
cfg,
|
|
201
|
+
logger: api.logger,
|
|
664
202
|
});
|
|
665
203
|
|
|
666
|
-
const
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
let ovSessionId = typeof ctx?.ovSessionId === "string" ? ctx.ovSessionId.trim() : "";
|
|
670
|
-
|
|
671
|
-
if (!ovSessionId && (sessionId || sessionKey)) {
|
|
672
|
-
ovSessionId = openClawSessionToOvStorageId(
|
|
673
|
-
sessionId || undefined,
|
|
674
|
-
sessionKey || undefined,
|
|
675
|
-
);
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
const session = {
|
|
679
|
-
agentId: ctx?.agentId,
|
|
680
|
-
sessionId: sessionId || undefined,
|
|
681
|
-
sessionKey: sessionKey || undefined,
|
|
682
|
-
ovSessionId: ovSessionId || undefined,
|
|
683
|
-
};
|
|
684
|
-
rememberSessionAgentId(session);
|
|
685
|
-
|
|
686
|
-
return {
|
|
687
|
-
sessionId: session.sessionId,
|
|
688
|
-
sessionKey: session.sessionKey,
|
|
689
|
-
ovSessionId: session.ovSessionId,
|
|
690
|
-
agentId: resolveAgentId(session.sessionId, session.sessionKey, session.ovSessionId),
|
|
691
|
-
};
|
|
692
|
-
};
|
|
693
|
-
|
|
694
|
-
const formatResourceImportText = (result: AddResourceResult): string => {
|
|
695
|
-
const root = result.root_uri ? ` ${result.root_uri}` : "";
|
|
696
|
-
const warnings = result.warnings?.length ? ` Warnings: ${result.warnings.join("; ")}` : "";
|
|
697
|
-
return `Imported OpenViking resource.${root}${warnings}`.trim();
|
|
698
|
-
};
|
|
699
|
-
|
|
700
|
-
const formatSkillImportText = (result: AddSkillResult): string => {
|
|
701
|
-
const uri = result.uri ? ` ${result.uri}` : "";
|
|
702
|
-
const name = result.name ? ` (${result.name})` : "";
|
|
703
|
-
return `Imported OpenViking skill${name}.${uri}`.trim();
|
|
704
|
-
};
|
|
705
|
-
|
|
706
|
-
const importResource = async (input: AddResourceInput, agentId?: string) => {
|
|
707
|
-
const client = await getClient();
|
|
708
|
-
const result = await client.addResource(input, agentId);
|
|
709
|
-
return {
|
|
710
|
-
content: [{ type: "text" as const, text: formatResourceImportText(result) }],
|
|
711
|
-
details: {
|
|
712
|
-
action: "resource_imported",
|
|
713
|
-
...result,
|
|
714
|
-
},
|
|
715
|
-
};
|
|
716
|
-
};
|
|
717
|
-
|
|
718
|
-
const importSkill = async (input: AddSkillInput, agentId?: string) => {
|
|
719
|
-
const client = await getClient();
|
|
720
|
-
const result = await client.addSkill(input, agentId);
|
|
721
|
-
return {
|
|
722
|
-
content: [{ type: "text" as const, text: formatSkillImportText(result) }],
|
|
723
|
-
details: {
|
|
724
|
-
action: "skill_imported",
|
|
725
|
-
...result,
|
|
726
|
-
},
|
|
727
|
-
};
|
|
728
|
-
};
|
|
729
|
-
|
|
730
|
-
const addResourceOpenViking = (input: AddResourceToolInput, agentId?: string) =>
|
|
731
|
-
importResource({
|
|
732
|
-
pathOrUrl: input.source ?? "",
|
|
733
|
-
to: input.to,
|
|
734
|
-
parent: input.parent,
|
|
735
|
-
reason: input.reason,
|
|
736
|
-
instruction: input.instruction,
|
|
737
|
-
wait: input.wait,
|
|
738
|
-
timeout: input.timeout,
|
|
739
|
-
}, agentId);
|
|
740
|
-
|
|
741
|
-
const addSkillOpenViking = (input: AddSkillToolInput, agentId?: string) =>
|
|
742
|
-
importSkill({
|
|
743
|
-
path: input.source,
|
|
744
|
-
data: input.data,
|
|
745
|
-
wait: input.wait,
|
|
746
|
-
timeout: input.timeout,
|
|
747
|
-
}, agentId);
|
|
748
|
-
|
|
749
|
-
const mergeFindResults = (results: FindResult[]): FindResult => {
|
|
750
|
-
const deduplicate = (items: FindResultItem[]): FindResultItem[] => {
|
|
751
|
-
const seen = new Map<string, FindResultItem>();
|
|
752
|
-
for (const item of items) {
|
|
753
|
-
if (!seen.has(item.uri)) {
|
|
754
|
-
seen.set(item.uri, item);
|
|
755
|
-
}
|
|
756
|
-
}
|
|
757
|
-
return Array.from(seen.values());
|
|
758
|
-
};
|
|
759
|
-
const memories = deduplicate(results.flatMap((result) => result.memories ?? []));
|
|
760
|
-
const resources = deduplicate(results.flatMap((result) => result.resources ?? []));
|
|
761
|
-
const skills = deduplicate(results.flatMap((result) => result.skills ?? []));
|
|
762
|
-
return {
|
|
763
|
-
memories,
|
|
764
|
-
resources,
|
|
765
|
-
skills,
|
|
766
|
-
total: memories.length + resources.length + skills.length,
|
|
767
|
-
};
|
|
768
|
-
};
|
|
769
|
-
|
|
770
|
-
const formatOVSearchRows = (result: FindResult): string[] => {
|
|
771
|
-
const truncateSummary = (value: string, maxChars = 220): string => {
|
|
772
|
-
const collapsed = value.replace(/\s+/g, " ").trim();
|
|
773
|
-
if (collapsed.length <= maxChars) {
|
|
774
|
-
return collapsed;
|
|
775
|
-
}
|
|
776
|
-
return `${collapsed.slice(0, maxChars - 3)}...`;
|
|
777
|
-
};
|
|
778
|
-
const truncateUri = (value: string, maxChars = 84): string => {
|
|
779
|
-
if (value.length <= maxChars) {
|
|
780
|
-
return value;
|
|
781
|
-
}
|
|
782
|
-
return `${value.slice(0, maxChars - 3)}...`;
|
|
783
|
-
};
|
|
784
|
-
const items = [
|
|
785
|
-
...(result.memories ?? []).map((item) => ({ contextType: "memory", item })),
|
|
786
|
-
...(result.resources ?? []).map((item) => ({ contextType: "resource", item })),
|
|
787
|
-
...(result.skills ?? []).map((item) => ({ contextType: "skill", item })),
|
|
788
|
-
];
|
|
789
|
-
if (items.length === 0) {
|
|
790
|
-
return [];
|
|
791
|
-
}
|
|
792
|
-
const numberHeader = "no";
|
|
793
|
-
const numberWidth = Math.max(numberHeader.length, String(items.length).length);
|
|
794
|
-
const typeWidth = Math.max("type".length, ...items.map(({ contextType }) => contextType.length));
|
|
795
|
-
const uriWidth = Math.max("uri".length, ...items.map(({ item }) => truncateUri(item.uri).length));
|
|
796
|
-
const levelWidth = Math.max("level".length, ...items.map(({ item }) => String(item.level ?? "").length));
|
|
797
|
-
const scoreWidth = Math.max(
|
|
798
|
-
"score".length,
|
|
799
|
-
...items.map(({ item }) => (typeof item.score === "number" ? item.score.toFixed(2).length : 0)),
|
|
800
|
-
);
|
|
801
|
-
return [
|
|
802
|
-
`${numberHeader.padEnd(numberWidth)} ${"type".padEnd(typeWidth)} ${"uri".padEnd(uriWidth)} ${"level".padEnd(levelWidth)} ${"score".padEnd(scoreWidth)} abstract`,
|
|
803
|
-
...items.map(({ contextType, item }, index) => {
|
|
804
|
-
const score = typeof item.score === "number" ? item.score.toFixed(2) : "";
|
|
805
|
-
const summary = truncateSummary(item.abstract || item.overview || "(no summary)");
|
|
806
|
-
return `${String(index + 1).padEnd(numberWidth)} ${contextType.padEnd(typeWidth)} ${truncateUri(item.uri).padEnd(uriWidth)} ${String(item.level ?? "").padEnd(levelWidth)} ${score.padEnd(scoreWidth)} ${summary}`;
|
|
807
|
-
}),
|
|
808
|
-
];
|
|
809
|
-
};
|
|
810
|
-
|
|
811
|
-
const formatOVSearchText = (query: string, uri: string | undefined, result: FindResult): string => {
|
|
812
|
-
if ((result.total ?? 0) <= 0) {
|
|
813
|
-
const scope = uri ? ` under ${uri}` : "";
|
|
814
|
-
return `No OpenViking resource or skill results found for "${query}"${scope}.`;
|
|
815
|
-
}
|
|
816
|
-
const scope = uri ? ` under ${uri}` : "";
|
|
817
|
-
const lines = [
|
|
818
|
-
`Found ${result.total ?? 0} OpenViking results for "${query}"${scope}`,
|
|
819
|
-
"",
|
|
820
|
-
...formatOVSearchRows(result),
|
|
821
|
-
].filter((line, index, all) => line || (all[index - 1] && all[index + 1]));
|
|
822
|
-
return lines.join("\n");
|
|
823
|
-
};
|
|
824
|
-
|
|
825
|
-
const searchOpenViking = async (input: OVSearchInput, agentId?: string) => {
|
|
826
|
-
const query = input.query.trim();
|
|
827
|
-
if (!query) {
|
|
828
|
-
throw new Error("query is required");
|
|
829
|
-
}
|
|
830
|
-
const limit = Math.max(1, Math.floor(input.limit ?? 10));
|
|
831
|
-
const client = await getClient();
|
|
832
|
-
let result: FindResult;
|
|
833
|
-
if (input.uri) {
|
|
834
|
-
result = await client.find(query, { targetUri: input.uri, limit }, agentId);
|
|
835
|
-
} else {
|
|
836
|
-
const [resourcesSettled, skillsSettled] = await Promise.allSettled([
|
|
837
|
-
client.find(query, { targetUri: "viking://resources", limit }, agentId),
|
|
838
|
-
client.find(query, { targetUri: "viking://agent/skills", limit }, agentId),
|
|
839
|
-
]);
|
|
840
|
-
const successful: FindResult[] = [];
|
|
841
|
-
if (resourcesSettled.status === "fulfilled") {
|
|
842
|
-
successful.push(resourcesSettled.value);
|
|
843
|
-
}
|
|
844
|
-
if (skillsSettled.status === "fulfilled") {
|
|
845
|
-
successful.push(skillsSettled.value);
|
|
846
|
-
}
|
|
847
|
-
if (successful.length === 0) {
|
|
848
|
-
const firstError =
|
|
849
|
-
resourcesSettled.status === "rejected"
|
|
850
|
-
? resourcesSettled.reason
|
|
851
|
-
: skillsSettled.status === "rejected"
|
|
852
|
-
? skillsSettled.reason
|
|
853
|
-
: "Both searches failed";
|
|
854
|
-
throw firstError instanceof Error ? firstError : new Error(String(firstError));
|
|
855
|
-
}
|
|
856
|
-
if (resourcesSettled.status === "rejected") {
|
|
857
|
-
api.logger.warn?.(`openviking: resource search failed: ${String(resourcesSettled.reason)}`);
|
|
858
|
-
}
|
|
859
|
-
if (skillsSettled.status === "rejected") {
|
|
860
|
-
api.logger.warn?.(`openviking: skill search failed: ${String(skillsSettled.reason)}`);
|
|
861
|
-
}
|
|
862
|
-
result = mergeFindResults(successful);
|
|
863
|
-
}
|
|
864
|
-
return {
|
|
865
|
-
content: [{ type: "text" as const, text: formatOVSearchText(query, input.uri, result) }],
|
|
866
|
-
details: {
|
|
867
|
-
action: "searched",
|
|
868
|
-
query,
|
|
869
|
-
uri: input.uri,
|
|
870
|
-
memories: result.memories ?? [],
|
|
871
|
-
resources: result.resources ?? [],
|
|
872
|
-
skills: result.skills ?? [],
|
|
873
|
-
total: result.total ?? 0,
|
|
874
|
-
},
|
|
875
|
-
};
|
|
876
|
-
};
|
|
877
|
-
|
|
878
|
-
api.registerTool(
|
|
879
|
-
(ctx: ToolContext) => ({
|
|
880
|
-
name: "add_resource",
|
|
881
|
-
label: "Add Resource (OpenViking)",
|
|
882
|
-
description:
|
|
883
|
-
"Use only when the user explicitly asks to import, add, upload, save, or index a document, directory, URL, Git repository, or OpenClaw media attachment into OpenViking resources. " +
|
|
884
|
-
"For a '[media attached: /path ...]' document, set source to that exact local media path. Do not invent OpenViking upload REST endpoints.",
|
|
885
|
-
parameters: Type.Object({
|
|
886
|
-
source: Type.String({ description: "Local path, OpenClaw media attachment path, directory path, public URL, or Git URL" }),
|
|
887
|
-
to: Type.Optional(Type.String({ description: "Exact target URI, e.g. viking://resources/project-docs" })),
|
|
888
|
-
parent: Type.Optional(Type.String({ description: "Parent URI under viking://resources" })),
|
|
889
|
-
reason: Type.Optional(Type.String({ description: "Reason or note for adding this resource" })),
|
|
890
|
-
instruction: Type.Optional(Type.String({ description: "Processing instruction for semantic extraction" })),
|
|
891
|
-
wait: Type.Optional(Type.Boolean({ description: "Wait for processing to complete" })),
|
|
892
|
-
timeout: Type.Optional(Type.Number({ description: "Timeout in seconds when wait is true" })),
|
|
893
|
-
}),
|
|
894
|
-
async execute(_toolCallId: string, params: Record<string, unknown>) {
|
|
895
|
-
if (isBypassedSession(ctx)) {
|
|
896
|
-
return makeBypassedToolResult("add_resource");
|
|
897
|
-
}
|
|
898
|
-
const session = resolvePluginSessionRouting(ctx);
|
|
899
|
-
return addResourceOpenViking({
|
|
900
|
-
source: typeof params.source === "string" ? params.source : undefined,
|
|
901
|
-
to: typeof params.to === "string" ? params.to : undefined,
|
|
902
|
-
parent: typeof params.parent === "string" ? params.parent : undefined,
|
|
903
|
-
reason: typeof params.reason === "string" ? params.reason : undefined,
|
|
904
|
-
instruction: typeof params.instruction === "string" ? params.instruction : undefined,
|
|
905
|
-
wait: typeof params.wait === "boolean" ? params.wait : undefined,
|
|
906
|
-
timeout: typeof params.timeout === "number" ? params.timeout : undefined,
|
|
907
|
-
}, session.agentId);
|
|
908
|
-
},
|
|
909
|
-
}),
|
|
910
|
-
{ name: "add_resource" },
|
|
911
|
-
);
|
|
912
|
-
|
|
913
|
-
api.registerTool(
|
|
914
|
-
(ctx: ToolContext) => ({
|
|
915
|
-
name: "add_skill",
|
|
916
|
-
label: "Add Skill (OpenViking)",
|
|
917
|
-
description:
|
|
918
|
-
"Use only when the user explicitly asks to import, add, install, or register a skill into OpenViking. " +
|
|
919
|
-
"Set source to a local SKILL.md file or skill directory, or data to raw SKILL.md content or an MCP tool dict.",
|
|
920
|
-
parameters: Type.Object({
|
|
921
|
-
source: Type.Optional(Type.String({ description: "Local SKILL.md path or skill directory path" })),
|
|
922
|
-
data: Type.Optional(Type.Any({ description: "Raw SKILL.md content or MCP tool dict" })),
|
|
923
|
-
wait: Type.Optional(Type.Boolean({ description: "Wait for processing to complete" })),
|
|
924
|
-
timeout: Type.Optional(Type.Number({ description: "Timeout in seconds when wait is true" })),
|
|
925
|
-
}),
|
|
926
|
-
async execute(_toolCallId: string, params: Record<string, unknown>) {
|
|
927
|
-
if (isBypassedSession(ctx)) {
|
|
928
|
-
return makeBypassedToolResult("add_skill");
|
|
929
|
-
}
|
|
930
|
-
const session = resolvePluginSessionRouting(ctx);
|
|
931
|
-
return addSkillOpenViking({
|
|
932
|
-
source: typeof params.source === "string" ? params.source : undefined,
|
|
933
|
-
data: params.data,
|
|
934
|
-
wait: typeof params.wait === "boolean" ? params.wait : undefined,
|
|
935
|
-
timeout: typeof params.timeout === "number" ? params.timeout : undefined,
|
|
936
|
-
}, session.agentId);
|
|
937
|
-
},
|
|
938
|
-
}),
|
|
939
|
-
{ name: "add_skill" },
|
|
940
|
-
);
|
|
941
|
-
|
|
942
|
-
api.registerTool(
|
|
943
|
-
(ctx: ToolContext) => ({
|
|
944
|
-
name: "ov_search",
|
|
945
|
-
label: "Search (OpenViking)",
|
|
946
|
-
description:
|
|
947
|
-
"Search OpenViking resources and skills. Use after importing, or when the user asks to search OpenViking resources or skills.",
|
|
948
|
-
parameters: Type.Object({
|
|
949
|
-
query: Type.String({ description: "Search query" }),
|
|
950
|
-
uri: Type.Optional(Type.String({ description: "Optional search URI. Defaults to resources plus agent skills." })),
|
|
951
|
-
limit: Type.Optional(Type.Number({ description: "Max results per search scope. Default: 10" })),
|
|
952
|
-
}),
|
|
953
|
-
async execute(_toolCallId: string, params: Record<string, unknown>) {
|
|
954
|
-
if (isBypassedSession(ctx)) {
|
|
955
|
-
return makeBypassedToolResult("ov_search");
|
|
956
|
-
}
|
|
957
|
-
const session = resolvePluginSessionRouting(ctx);
|
|
958
|
-
return searchOpenViking({
|
|
959
|
-
query: String((params as { query?: unknown }).query ?? ""),
|
|
960
|
-
uri: typeof params.uri === "string" ? params.uri : undefined,
|
|
961
|
-
limit: typeof params.limit === "number" ? params.limit : undefined,
|
|
962
|
-
}, session.agentId);
|
|
963
|
-
},
|
|
964
|
-
}),
|
|
965
|
-
{ name: "ov_search" },
|
|
966
|
-
);
|
|
967
|
-
|
|
968
|
-
api.registerCommand?.({
|
|
969
|
-
name: "add-resource",
|
|
970
|
-
description: "Add a resource into OpenViking.",
|
|
971
|
-
acceptsArgs: true,
|
|
972
|
-
handler: async (ctx: PluginCommandContext) => {
|
|
973
|
-
try {
|
|
974
|
-
if (isBypassedSession(ctx)) {
|
|
975
|
-
const bypassed = makeBypassedToolResult("add_resource");
|
|
976
|
-
return { text: bypassed.content[0]!.text, details: bypassed.details };
|
|
977
|
-
}
|
|
978
|
-
const session = resolvePluginSessionRouting(ctx);
|
|
979
|
-
const input = parseAddResourceCommandArgs(ctx.args ?? "");
|
|
980
|
-
const result = await addResourceOpenViking(input, session.agentId);
|
|
981
|
-
return { text: result.content[0]!.text, details: result.details };
|
|
982
|
-
} catch (err) {
|
|
983
|
-
return { text: `OpenViking add resource failed: ${err instanceof Error ? err.message : String(err)}` };
|
|
984
|
-
}
|
|
985
|
-
},
|
|
204
|
+
const { queryConfigStore, traceRecorder } = createOpenVikingRuntimeState({
|
|
205
|
+
cfg,
|
|
206
|
+
logger: api.logger,
|
|
986
207
|
});
|
|
987
208
|
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
}
|
|
998
|
-
const session = resolvePluginSessionRouting(ctx);
|
|
999
|
-
const input = parseAddSkillCommandArgs(ctx.args ?? "");
|
|
1000
|
-
const result = await addSkillOpenViking(input, session.agentId);
|
|
1001
|
-
return { text: result.content[0]!.text, details: result.details };
|
|
1002
|
-
} catch (err) {
|
|
1003
|
-
return { text: `OpenViking add skill failed: ${err instanceof Error ? err.message : String(err)}` };
|
|
1004
|
-
}
|
|
1005
|
-
},
|
|
209
|
+
const {
|
|
210
|
+
rememberSessionAgentId,
|
|
211
|
+
resolveAgentId,
|
|
212
|
+
resolvePluginSessionRouting,
|
|
213
|
+
toQueryConfigContext,
|
|
214
|
+
} = createOpenVikingSessionRoutingRuntime({
|
|
215
|
+
peerPrefix: cfg.peer_prefix,
|
|
216
|
+
logFindRequests: cfg.logFindRequests,
|
|
217
|
+
logger: api.logger,
|
|
1006
218
|
});
|
|
1007
219
|
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
}
|
|
1018
|
-
const session = resolvePluginSessionRouting(ctx);
|
|
1019
|
-
const input = parseOVSearchCommandArgs(ctx.args ?? "");
|
|
1020
|
-
const result = await searchOpenViking(input, session.agentId);
|
|
1021
|
-
return { text: result.content[0]!.text, details: result.details };
|
|
1022
|
-
} catch (err) {
|
|
1023
|
-
return { text: `OpenViking search failed: ${err instanceof Error ? err.message : String(err)}` };
|
|
1024
|
-
}
|
|
1025
|
-
},
|
|
220
|
+
const recallTraceRuntime = createOpenVikingRecallTraceRuntime({
|
|
221
|
+
getClient,
|
|
222
|
+
resolvePluginSessionRouting,
|
|
223
|
+
traceRecorder,
|
|
224
|
+
registerRecallTraceRoutes: registerRecallTraceRouteAdapters,
|
|
225
|
+
normalizeResourceTypes,
|
|
226
|
+
clampScore,
|
|
227
|
+
previewText,
|
|
228
|
+
cfg,
|
|
1026
229
|
});
|
|
230
|
+
const { queryRecallTraces, formatRecallTraceText, registerRecallTraceRoutes } = recallTraceRuntime;
|
|
1027
231
|
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
parameters: Type.Object({
|
|
1035
|
-
query: Type.String({ description: "Search query" }),
|
|
1036
|
-
limit: Type.Optional(
|
|
1037
|
-
Type.Number({ description: "Max results (default: plugin config)" }),
|
|
1038
|
-
),
|
|
1039
|
-
scoreThreshold: Type.Optional(
|
|
1040
|
-
Type.Number({ description: "Minimum score (0-1, default: plugin config)" }),
|
|
1041
|
-
),
|
|
1042
|
-
targetUri: Type.Optional(
|
|
1043
|
-
Type.String({ description: "Search scope URI (default: plugin config)" }),
|
|
1044
|
-
),
|
|
1045
|
-
}),
|
|
1046
|
-
async execute(_toolCallId: string, params: Record<string, unknown>) {
|
|
1047
|
-
if (isBypassedSession(ctx)) {
|
|
1048
|
-
return makeBypassedToolResult("memory_recall");
|
|
1049
|
-
}
|
|
1050
|
-
const session = resolvePluginSessionRouting(ctx);
|
|
1051
|
-
const { query } = params as { query: string };
|
|
1052
|
-
const limit =
|
|
1053
|
-
typeof (params as { limit?: number }).limit === "number"
|
|
1054
|
-
? Math.max(1, Math.floor((params as { limit: number }).limit))
|
|
1055
|
-
: cfg.recallLimit;
|
|
1056
|
-
const scoreThreshold =
|
|
1057
|
-
typeof (params as { scoreThreshold?: number }).scoreThreshold === "number"
|
|
1058
|
-
? Math.max(0, Math.min(1, (params as { scoreThreshold: number }).scoreThreshold))
|
|
1059
|
-
: cfg.recallScoreThreshold;
|
|
1060
|
-
const targetUri =
|
|
1061
|
-
typeof (params as { targetUri?: string }).targetUri === "string"
|
|
1062
|
-
? (params as { targetUri: string }).targetUri
|
|
1063
|
-
: undefined;
|
|
1064
|
-
const requestLimit = Math.max(limit * 4, 20);
|
|
1065
|
-
|
|
1066
|
-
const recallClient = await getClient();
|
|
1067
|
-
if (cfg.logFindRequests) {
|
|
1068
|
-
api.logger.info(
|
|
1069
|
-
`openviking: memory_recall X-OpenViking-Agent="${session.agentId}" ` +
|
|
1070
|
-
`(plugin defaultAgentId="${recallClient.getDefaultAgentId()}" is unused when session context is present)`,
|
|
1071
|
-
);
|
|
1072
|
-
}
|
|
1073
|
-
|
|
1074
|
-
let result;
|
|
1075
|
-
if (targetUri) {
|
|
1076
|
-
// 如果指定了目标 URI,只检索该位置
|
|
1077
|
-
result = await recallClient.find(
|
|
1078
|
-
query,
|
|
1079
|
-
{
|
|
1080
|
-
targetUri,
|
|
1081
|
-
limit: requestLimit,
|
|
1082
|
-
scoreThreshold: 0,
|
|
1083
|
-
},
|
|
1084
|
-
session.agentId,
|
|
1085
|
-
);
|
|
1086
|
-
} else {
|
|
1087
|
-
const searchPromises: Promise<FindResult>[] = [
|
|
1088
|
-
recallClient.find(
|
|
1089
|
-
query,
|
|
1090
|
-
{
|
|
1091
|
-
targetUri: "viking://user/memories",
|
|
1092
|
-
limit: requestLimit,
|
|
1093
|
-
scoreThreshold: 0,
|
|
1094
|
-
},
|
|
1095
|
-
session.agentId,
|
|
1096
|
-
),
|
|
1097
|
-
recallClient.find(
|
|
1098
|
-
query,
|
|
1099
|
-
{
|
|
1100
|
-
targetUri: "viking://agent/memories",
|
|
1101
|
-
limit: requestLimit,
|
|
1102
|
-
scoreThreshold: 0,
|
|
1103
|
-
},
|
|
1104
|
-
session.agentId,
|
|
1105
|
-
),
|
|
1106
|
-
];
|
|
1107
|
-
if (cfg.recallResources) {
|
|
1108
|
-
searchPromises.push(
|
|
1109
|
-
recallClient.find(
|
|
1110
|
-
query,
|
|
1111
|
-
{
|
|
1112
|
-
targetUri: "viking://resources",
|
|
1113
|
-
limit: requestLimit,
|
|
1114
|
-
scoreThreshold: 0,
|
|
1115
|
-
},
|
|
1116
|
-
session.agentId,
|
|
1117
|
-
),
|
|
1118
|
-
);
|
|
1119
|
-
}
|
|
1120
|
-
const settled = await Promise.allSettled(searchPromises);
|
|
1121
|
-
const allMemories: FindResultItem[] = [];
|
|
1122
|
-
for (const s of settled) {
|
|
1123
|
-
if (s.status === "fulfilled") {
|
|
1124
|
-
allMemories.push(...(s.value.memories ?? []), ...(s.value.resources ?? []));
|
|
1125
|
-
}
|
|
1126
|
-
}
|
|
1127
|
-
const uniqueMemories = allMemories.filter((memory, index, self) =>
|
|
1128
|
-
index === self.findIndex((m) => m.uri === memory.uri)
|
|
1129
|
-
);
|
|
1130
|
-
const leafOnly = uniqueMemories.filter((m) => !m.level || m.level === 2);
|
|
1131
|
-
result = {
|
|
1132
|
-
memories: leafOnly,
|
|
1133
|
-
total: leafOnly.length,
|
|
1134
|
-
};
|
|
1135
|
-
}
|
|
1136
|
-
|
|
1137
|
-
const leafOnly = (result.memories ?? []).filter((m) => !m.level || m.level === 2);
|
|
1138
|
-
const processed = postProcessMemories(leafOnly, {
|
|
1139
|
-
limit: requestLimit,
|
|
1140
|
-
scoreThreshold,
|
|
1141
|
-
});
|
|
1142
|
-
const memories = pickMemoriesForInjection(processed, limit, query);
|
|
1143
|
-
if (memories.length === 0) {
|
|
1144
|
-
return {
|
|
1145
|
-
content: [{ type: "text", text: "No relevant OpenViking memories found." }],
|
|
1146
|
-
details: { count: 0, total: result.total ?? 0, scoreThreshold },
|
|
1147
|
-
};
|
|
1148
|
-
}
|
|
1149
|
-
const { lines: memoryLines } = await buildMemoryLinesWithBudget(
|
|
1150
|
-
memories,
|
|
1151
|
-
(uri) => recallClient.read(uri, session.agentId),
|
|
1152
|
-
{
|
|
1153
|
-
recallPreferAbstract: false,
|
|
1154
|
-
recallMaxInjectedChars: cfg.recallMaxInjectedChars,
|
|
1155
|
-
},
|
|
1156
|
-
);
|
|
1157
|
-
if (memoryLines.length === 0) {
|
|
1158
|
-
return {
|
|
1159
|
-
content: [
|
|
1160
|
-
{
|
|
1161
|
-
type: "text",
|
|
1162
|
-
text: `No complete OpenViking memories fit recallMaxInjectedChars=${cfg.recallMaxInjectedChars}.`,
|
|
1163
|
-
},
|
|
1164
|
-
],
|
|
1165
|
-
details: {
|
|
1166
|
-
count: 0,
|
|
1167
|
-
memories,
|
|
1168
|
-
total: result.total ?? memories.length,
|
|
1169
|
-
scoreThreshold,
|
|
1170
|
-
requestLimit,
|
|
1171
|
-
recallMaxInjectedChars: cfg.recallMaxInjectedChars,
|
|
1172
|
-
},
|
|
1173
|
-
};
|
|
1174
|
-
}
|
|
1175
|
-
return {
|
|
1176
|
-
content: [
|
|
1177
|
-
{
|
|
1178
|
-
type: "text",
|
|
1179
|
-
text: `Found ${memoryLines.length} memories:\n\n${memoryLines.join("\n")}`,
|
|
1180
|
-
},
|
|
1181
|
-
],
|
|
1182
|
-
details: {
|
|
1183
|
-
count: memoryLines.length,
|
|
1184
|
-
memories,
|
|
1185
|
-
total: result.total ?? memories.length,
|
|
1186
|
-
scoreThreshold,
|
|
1187
|
-
requestLimit,
|
|
1188
|
-
recallMaxInjectedChars: cfg.recallMaxInjectedChars,
|
|
1189
|
-
},
|
|
1190
|
-
};
|
|
1191
|
-
},
|
|
1192
|
-
}),
|
|
1193
|
-
{ name: "memory_recall" },
|
|
1194
|
-
);
|
|
1195
|
-
|
|
1196
|
-
api.registerTool(
|
|
1197
|
-
(ctx: ToolContext) => ({
|
|
1198
|
-
name: "memory_store",
|
|
1199
|
-
label: "Memory Store (OpenViking)",
|
|
1200
|
-
description:
|
|
1201
|
-
"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.",
|
|
1202
|
-
parameters: Type.Object({
|
|
1203
|
-
text: Type.String({ description: "Information to store as memory source text" }),
|
|
1204
|
-
role: Type.Optional(Type.String({ description: "Session role, default user" })),
|
|
1205
|
-
sessionId: Type.Optional(Type.String({ description: "Existing OpenViking session ID" })),
|
|
1206
|
-
}),
|
|
1207
|
-
async execute(_toolCallId: string, params: Record<string, unknown>) {
|
|
1208
|
-
if (isBypassedSession(ctx)) {
|
|
1209
|
-
return makeBypassedToolResult("memory_store");
|
|
1210
|
-
}
|
|
1211
|
-
const session = resolvePluginSessionRouting(ctx);
|
|
1212
|
-
const { text } = params as { text: string };
|
|
1213
|
-
const role =
|
|
1214
|
-
typeof (params as { role?: string }).role === "string"
|
|
1215
|
-
? (params as { role: string }).role
|
|
1216
|
-
: "user";
|
|
1217
|
-
const explicitSessionId =
|
|
1218
|
-
typeof (params as { sessionId?: unknown }).sessionId === "string" &&
|
|
1219
|
-
(params as { sessionId: string }).sessionId.trim()
|
|
1220
|
-
? openClawSessionRefToOvStorageId((params as { sessionId: string }).sessionId)
|
|
1221
|
-
: undefined;
|
|
1222
|
-
|
|
1223
|
-
if (cfg.logFindRequests) {
|
|
1224
|
-
api.logger.info?.(
|
|
1225
|
-
`openviking: memory_store invoked (textLength=${text?.length ?? 0}, sessionId=${explicitSessionId ?? "auto"})`,
|
|
1226
|
-
);
|
|
1227
|
-
}
|
|
1228
|
-
|
|
1229
|
-
let sessionId = explicitSessionId;
|
|
1230
|
-
let usedTempSession = false;
|
|
1231
|
-
try {
|
|
1232
|
-
const c = await getClient();
|
|
1233
|
-
if (!sessionId) {
|
|
1234
|
-
sessionId = `memory-store-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
1235
|
-
usedTempSession = true;
|
|
1236
|
-
}
|
|
1237
|
-
const roleId = role === "user" ? toRoleId(extractToolSenderId(ctx)) : undefined;
|
|
1238
|
-
await c.addSessionMessage(
|
|
1239
|
-
sessionId,
|
|
1240
|
-
role,
|
|
1241
|
-
[{ type: "text" as const, text }],
|
|
1242
|
-
session.agentId,
|
|
1243
|
-
undefined,
|
|
1244
|
-
roleId,
|
|
1245
|
-
);
|
|
1246
|
-
const commitResult = await c.commitSession(sessionId, {
|
|
1247
|
-
wait: true,
|
|
1248
|
-
agentId: session.agentId,
|
|
1249
|
-
keepRecentCount: 0,
|
|
1250
|
-
});
|
|
1251
|
-
const memoriesCount = totalCommitMemories(commitResult);
|
|
1252
|
-
if (commitResult.status === "failed") {
|
|
1253
|
-
api.logger.warn(
|
|
1254
|
-
`openviking: memory_store commit failed (sessionId=${sessionId}): ${commitResult.error ?? "unknown"}`,
|
|
1255
|
-
);
|
|
1256
|
-
return {
|
|
1257
|
-
content: [{ type: "text", text: `Memory extraction failed for session ${sessionId}: ${commitResult.error ?? "unknown"}` }],
|
|
1258
|
-
details: {
|
|
1259
|
-
action: "failed",
|
|
1260
|
-
sessionId,
|
|
1261
|
-
status: "failed",
|
|
1262
|
-
error: commitResult.error,
|
|
1263
|
-
usedTempSession,
|
|
1264
|
-
},
|
|
1265
|
-
};
|
|
1266
|
-
}
|
|
1267
|
-
if (commitResult.status === "timeout") {
|
|
1268
|
-
api.logger.warn(
|
|
1269
|
-
`openviking: memory_store commit timed out (sessionId=${sessionId}), task_id=${commitResult.task_id ?? "none"}. Memories may still be extracting in background.`,
|
|
1270
|
-
);
|
|
1271
|
-
return {
|
|
1272
|
-
content: [{ type: "text", text: `Memory extraction timed out for session ${sessionId}. It may still complete in the background (task_id=${commitResult.task_id ?? "none"}).` }],
|
|
1273
|
-
details: {
|
|
1274
|
-
action: "timeout",
|
|
1275
|
-
sessionId,
|
|
1276
|
-
status: "timeout",
|
|
1277
|
-
taskId: commitResult.task_id,
|
|
1278
|
-
usedTempSession,
|
|
1279
|
-
},
|
|
1280
|
-
};
|
|
1281
|
-
}
|
|
1282
|
-
if (memoriesCount === 0) {
|
|
1283
|
-
api.logger.warn(
|
|
1284
|
-
`openviking: memory_store committed but 0 memories extracted (sessionId=${sessionId}). ` +
|
|
1285
|
-
"No OpenViking-managed long-term memory was created. Check memory.extraction_enabled, VLM configuration/API keys, or whether the text was judged not durable.",
|
|
1286
|
-
);
|
|
1287
|
-
return {
|
|
1288
|
-
content: [
|
|
1289
|
-
{
|
|
1290
|
-
type: "text",
|
|
1291
|
-
text:
|
|
1292
|
-
`Memory extraction completed for session ${sessionId}, but produced 0 memories. ` +
|
|
1293
|
-
"No OpenViking-managed long-term memory was stored. Check memory.extraction_enabled, VLM configuration/API keys, or whether the text is durable enough to remember.",
|
|
1294
|
-
},
|
|
1295
|
-
],
|
|
1296
|
-
details: {
|
|
1297
|
-
action: "failed",
|
|
1298
|
-
sessionId,
|
|
1299
|
-
status: commitResult.status,
|
|
1300
|
-
error: "no_memories_extracted",
|
|
1301
|
-
memoriesCount,
|
|
1302
|
-
archived: commitResult.archived ?? false,
|
|
1303
|
-
usedTempSession,
|
|
1304
|
-
},
|
|
1305
|
-
};
|
|
1306
|
-
} else {
|
|
1307
|
-
api.logger.info?.(`openviking: memory_store committed, memories=${memoriesCount}`);
|
|
1308
|
-
}
|
|
1309
|
-
return {
|
|
1310
|
-
content: [
|
|
1311
|
-
{
|
|
1312
|
-
type: "text",
|
|
1313
|
-
text: `Stored in OpenViking session ${sessionId} and committed ${memoriesCount} memories.`,
|
|
1314
|
-
},
|
|
1315
|
-
],
|
|
1316
|
-
details: {
|
|
1317
|
-
action: "stored",
|
|
1318
|
-
sessionId,
|
|
1319
|
-
memoriesCount,
|
|
1320
|
-
status: commitResult.status,
|
|
1321
|
-
archived: commitResult.archived ?? false,
|
|
1322
|
-
usedTempSession,
|
|
1323
|
-
},
|
|
1324
|
-
};
|
|
1325
|
-
} catch (err) {
|
|
1326
|
-
api.logger.warn(`openviking: memory_store failed: ${String(err)}`);
|
|
1327
|
-
throw err;
|
|
1328
|
-
}
|
|
1329
|
-
},
|
|
1330
|
-
}),
|
|
1331
|
-
{ name: "memory_store" },
|
|
1332
|
-
);
|
|
1333
|
-
|
|
1334
|
-
api.registerTool(
|
|
1335
|
-
(ctx: ToolContext) => ({
|
|
1336
|
-
name: "memory_forget",
|
|
1337
|
-
label: "Memory Forget (OpenViking)",
|
|
1338
|
-
description:
|
|
1339
|
-
"Forget memory by URI, or search then delete when a strong single match is found.",
|
|
1340
|
-
parameters: Type.Object({
|
|
1341
|
-
uri: Type.Optional(Type.String({ description: "Exact memory URI to delete" })),
|
|
1342
|
-
query: Type.Optional(Type.String({ description: "Search query to find memory URI" })),
|
|
1343
|
-
targetUri: Type.Optional(
|
|
1344
|
-
Type.String({ description: "Search scope URI (default: plugin config)" }),
|
|
1345
|
-
),
|
|
1346
|
-
limit: Type.Optional(Type.Number({ description: "Search limit (default: 5)" })),
|
|
1347
|
-
scoreThreshold: Type.Optional(
|
|
1348
|
-
Type.Number({ description: "Minimum score (0-1, default: plugin config)" }),
|
|
1349
|
-
),
|
|
1350
|
-
}),
|
|
1351
|
-
async execute(_toolCallId: string, params: Record<string, unknown>) {
|
|
1352
|
-
if (isBypassedSession(ctx)) {
|
|
1353
|
-
return makeBypassedToolResult("memory_forget");
|
|
1354
|
-
}
|
|
1355
|
-
const session = resolvePluginSessionRouting(ctx);
|
|
1356
|
-
const client = await getClient();
|
|
1357
|
-
const uri = (params as { uri?: string }).uri;
|
|
1358
|
-
if (uri) {
|
|
1359
|
-
if (!isMemoryUri(uri)) {
|
|
1360
|
-
return {
|
|
1361
|
-
content: [{ type: "text", text: `Refusing to delete non-memory URI: ${uri}` }],
|
|
1362
|
-
details: { action: "rejected", uri },
|
|
1363
|
-
};
|
|
1364
|
-
}
|
|
1365
|
-
await client.deleteUri(uri, session.agentId);
|
|
1366
|
-
return {
|
|
1367
|
-
content: [{ type: "text", text: `Forgotten: ${uri}` }],
|
|
1368
|
-
details: { action: "deleted", uri },
|
|
1369
|
-
};
|
|
1370
|
-
}
|
|
1371
|
-
|
|
1372
|
-
const query = (params as { query?: string }).query;
|
|
1373
|
-
if (!query) {
|
|
1374
|
-
return {
|
|
1375
|
-
content: [{ type: "text", text: "Provide uri or query." }],
|
|
1376
|
-
details: { error: "missing_param" },
|
|
1377
|
-
};
|
|
1378
|
-
}
|
|
1379
|
-
|
|
1380
|
-
const limit =
|
|
1381
|
-
typeof (params as { limit?: number }).limit === "number"
|
|
1382
|
-
? Math.max(1, Math.floor((params as { limit: number }).limit))
|
|
1383
|
-
: 5;
|
|
1384
|
-
const scoreThreshold =
|
|
1385
|
-
typeof (params as { scoreThreshold?: number }).scoreThreshold === "number"
|
|
1386
|
-
? Math.max(0, Math.min(1, (params as { scoreThreshold: number }).scoreThreshold))
|
|
1387
|
-
: cfg.recallScoreThreshold;
|
|
1388
|
-
const targetUri =
|
|
1389
|
-
typeof (params as { targetUri?: string }).targetUri === "string"
|
|
1390
|
-
? (params as { targetUri: string }).targetUri
|
|
1391
|
-
: cfg.targetUri;
|
|
1392
|
-
const requestLimit = Math.max(limit * 4, 20);
|
|
1393
|
-
|
|
1394
|
-
const result = await client.find(
|
|
1395
|
-
query,
|
|
1396
|
-
{
|
|
1397
|
-
targetUri,
|
|
1398
|
-
limit: requestLimit,
|
|
1399
|
-
scoreThreshold: 0,
|
|
1400
|
-
},
|
|
1401
|
-
session.agentId,
|
|
1402
|
-
);
|
|
1403
|
-
const candidates = postProcessMemories(result.memories ?? [], {
|
|
1404
|
-
limit: requestLimit,
|
|
1405
|
-
scoreThreshold,
|
|
1406
|
-
leafOnly: true,
|
|
1407
|
-
}).filter((item) => isMemoryUri(item.uri));
|
|
1408
|
-
if (candidates.length === 0) {
|
|
1409
|
-
return {
|
|
1410
|
-
content: [
|
|
1411
|
-
{
|
|
1412
|
-
type: "text",
|
|
1413
|
-
text: "No matching leaf memory candidates found. Try a more specific query.",
|
|
1414
|
-
},
|
|
1415
|
-
],
|
|
1416
|
-
details: { action: "none", scoreThreshold },
|
|
1417
|
-
};
|
|
1418
|
-
}
|
|
1419
|
-
const top = candidates[0];
|
|
1420
|
-
if (candidates.length === 1 && clampScore(top.score) >= 0.85) {
|
|
1421
|
-
await client.deleteUri(top.uri, session.agentId);
|
|
1422
|
-
return {
|
|
1423
|
-
content: [{ type: "text", text: `Forgotten: ${top.uri}` }],
|
|
1424
|
-
details: { action: "deleted", uri: top.uri, score: top.score ?? 0 },
|
|
1425
|
-
};
|
|
1426
|
-
}
|
|
1427
|
-
|
|
1428
|
-
const list = candidates
|
|
1429
|
-
.map((item) => `- ${item.uri} (${(clampScore(item.score) * 100).toFixed(0)}%)`)
|
|
1430
|
-
.join("\n");
|
|
1431
|
-
|
|
1432
|
-
return {
|
|
1433
|
-
content: [
|
|
1434
|
-
{
|
|
1435
|
-
type: "text",
|
|
1436
|
-
text: `Found ${candidates.length} candidates. Specify uri:\n${list}`,
|
|
1437
|
-
},
|
|
1438
|
-
],
|
|
1439
|
-
details: { action: "candidates", candidates, scoreThreshold, requestLimit },
|
|
1440
|
-
};
|
|
1441
|
-
},
|
|
1442
|
-
}),
|
|
1443
|
-
{ name: "memory_forget" },
|
|
1444
|
-
);
|
|
1445
|
-
|
|
1446
|
-
api.registerTool(
|
|
1447
|
-
(ctx: ToolContext) => ({
|
|
1448
|
-
name: "ov_archive_search",
|
|
1449
|
-
label: "Archive Search (OpenViking)",
|
|
1450
|
-
description:
|
|
1451
|
-
"Keyword-grep across all archived original conversation messages of the current session. " +
|
|
1452
|
-
"Use this whenever the [Session History Summary] does not contain the specific detail " +
|
|
1453
|
-
"the user is asking about. Extract 2-3 concrete entity words from the question " +
|
|
1454
|
-
"(names, places, objects, dates) and search each separately. " +
|
|
1455
|
-
"Only conclude information is unavailable after trying at least 2 different keyword variations.",
|
|
1456
|
-
parameters: Type.Object({
|
|
1457
|
-
query: Type.String({
|
|
1458
|
-
description:
|
|
1459
|
-
"A single keyword or short phrase to grep. Use concrete nouns, names, dates, " +
|
|
1460
|
-
"or distinctive phrases. Case-insensitive. Prefer entity words over full sentences.",
|
|
1461
|
-
}),
|
|
1462
|
-
archiveId: Type.Optional(
|
|
1463
|
-
Type.String({
|
|
1464
|
-
description: 'Optional: limit search to one archive (e.g. "archive_005")',
|
|
1465
|
-
}),
|
|
1466
|
-
),
|
|
1467
|
-
}),
|
|
1468
|
-
async execute(_toolCallId: string, params: Record<string, unknown>) {
|
|
1469
|
-
if (isBypassedSession(ctx)) {
|
|
1470
|
-
return makeBypassedToolResult("ov_archive_search");
|
|
1471
|
-
}
|
|
1472
|
-
rememberSessionAgentId(ctx);
|
|
1473
|
-
const sessionId = ctx.sessionId ?? "";
|
|
1474
|
-
const sessionKey = ctx.sessionKey ?? "";
|
|
1475
|
-
if (!sessionId && !sessionKey) {
|
|
1476
|
-
return {
|
|
1477
|
-
content: [{ type: "text", text: "Error: no active session." }],
|
|
1478
|
-
details: { error: "no_session" },
|
|
1479
|
-
};
|
|
1480
|
-
}
|
|
1481
|
-
const ovSessionId = openClawSessionToOvStorageId(ctx.sessionId, ctx.sessionKey);
|
|
1482
|
-
const query = String((params as { query?: string }).query ?? "").trim();
|
|
1483
|
-
const archiveId = (params as { archiveId?: string }).archiveId;
|
|
1484
|
-
|
|
1485
|
-
if (!query) {
|
|
1486
|
-
return {
|
|
1487
|
-
content: [{ type: "text", text: "Error: query is required." }],
|
|
1488
|
-
details: { error: "missing_param", param: "query" },
|
|
1489
|
-
};
|
|
1490
|
-
}
|
|
1491
|
-
|
|
1492
|
-
const escapedQuery = query.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1493
|
-
api.logger.info?.(`openviking: ov_archive_search query="${query}" escaped="${escapedQuery}" archive=${archiveId ?? "all"} session=${ovSessionId}`);
|
|
1494
|
-
|
|
1495
|
-
try {
|
|
1496
|
-
const client = await getClient();
|
|
1497
|
-
const agentId = resolveAgentId(ctx.sessionId, ctx.sessionKey);
|
|
1498
|
-
const result = await client.grepSessionArchives(ovSessionId, escapedQuery, {
|
|
1499
|
-
archiveId,
|
|
1500
|
-
caseInsensitive: true,
|
|
1501
|
-
agentId,
|
|
1502
|
-
});
|
|
1503
|
-
|
|
1504
|
-
if (!result.matches || result.matches.length === 0) {
|
|
1505
|
-
return {
|
|
1506
|
-
content: [{
|
|
1507
|
-
type: "text",
|
|
1508
|
-
text: `No matches found for "${query}". Try a different keyword — ` +
|
|
1509
|
-
"the original conversation may use different wording than the question. " +
|
|
1510
|
-
"Try synonyms, related terms, or shorter fragments.",
|
|
1511
|
-
}],
|
|
1512
|
-
details: { query, matchCount: 0 },
|
|
1513
|
-
};
|
|
1514
|
-
}
|
|
1515
|
-
|
|
1516
|
-
const MAX_MATCHES = 12;
|
|
1517
|
-
const MAX_LINE_LEN = 1500;
|
|
1518
|
-
const shown = result.matches.slice(0, MAX_MATCHES);
|
|
1519
|
-
const blocks = shown.map((m, i) => {
|
|
1520
|
-
const archiveTag = m.uri.match(/archive_\d+/)?.[0] ?? "unknown";
|
|
1521
|
-
const truncated = m.content.length > MAX_LINE_LEN
|
|
1522
|
-
? m.content.slice(0, MAX_LINE_LEN) + "…(truncated)"
|
|
1523
|
-
: m.content;
|
|
1524
|
-
return `## Match ${i + 1}: ${archiveTag} (line ${m.line})\n${truncated}`;
|
|
1525
|
-
});
|
|
1526
|
-
|
|
1527
|
-
const header = `Found ${result.matches.length} match(es) for "${query}"` +
|
|
1528
|
-
(result.matches.length > MAX_MATCHES ? ` (showing first ${MAX_MATCHES})` : "") + ":";
|
|
1529
|
-
|
|
1530
|
-
return {
|
|
1531
|
-
content: [{ type: "text", text: header + "\n\n" + blocks.join("\n\n") }],
|
|
1532
|
-
details: { query, matchCount: result.matches.length },
|
|
1533
|
-
};
|
|
1534
|
-
} catch (err: unknown) {
|
|
1535
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
1536
|
-
api.logger.error?.(`openviking: ov_archive_search error: ${msg}`);
|
|
1537
|
-
return {
|
|
1538
|
-
content: [{ type: "text", text: `Archive search failed: ${msg}` }],
|
|
1539
|
-
details: { error: msg },
|
|
1540
|
-
};
|
|
1541
|
-
}
|
|
1542
|
-
},
|
|
1543
|
-
}),
|
|
1544
|
-
{ name: "ov_archive_search" },
|
|
1545
|
-
);
|
|
1546
|
-
|
|
1547
|
-
api.registerTool((ctx: ToolContext) => ({
|
|
1548
|
-
name: "ov_archive_expand",
|
|
1549
|
-
label: "Archive Expand (OpenViking)",
|
|
1550
|
-
description:
|
|
1551
|
-
"Retrieve original messages from a compressed session archive. " +
|
|
1552
|
-
"Use when a session summary lacks specific details " +
|
|
1553
|
-
"such as exact commands, file paths, code snippets, or config values. " +
|
|
1554
|
-
"Check [Archive Index] to find the right archive ID.",
|
|
1555
|
-
parameters: Type.Object({
|
|
1556
|
-
archiveId: Type.String({
|
|
1557
|
-
description:
|
|
1558
|
-
'Archive ID from [Archive Index] (e.g. "archive_002")',
|
|
1559
|
-
}),
|
|
1560
|
-
}),
|
|
1561
|
-
async execute(_toolCallId: string, params: Record<string, unknown>) {
|
|
1562
|
-
if (isBypassedSession(ctx)) {
|
|
1563
|
-
return makeBypassedToolResult("ov_archive_expand");
|
|
1564
|
-
}
|
|
1565
|
-
const session = resolvePluginSessionRouting(ctx);
|
|
1566
|
-
const archiveId = String((params as { archiveId?: string }).archiveId ?? "").trim();
|
|
1567
|
-
const sessionId = session.sessionId ?? "";
|
|
1568
|
-
api.logger.info?.(`openviking: ov_archive_expand invoked (archiveId=${archiveId || "(empty)"}, sessionId=${sessionId || "(empty)"})`);
|
|
1569
|
-
|
|
1570
|
-
if (!archiveId) {
|
|
1571
|
-
api.logger.warn?.(`openviking: ov_archive_expand missing archiveId`);
|
|
1572
|
-
return {
|
|
1573
|
-
content: [{ type: "text", text: "Error: archiveId is required." }],
|
|
1574
|
-
details: { error: "missing_param", param: "archiveId" },
|
|
1575
|
-
};
|
|
1576
|
-
}
|
|
1577
|
-
|
|
1578
|
-
if (!session.ovSessionId) {
|
|
1579
|
-
return {
|
|
1580
|
-
content: [{ type: "text", text: "Error: no active session." }],
|
|
1581
|
-
details: { error: "no_session" },
|
|
1582
|
-
};
|
|
1583
|
-
}
|
|
1584
|
-
|
|
1585
|
-
try {
|
|
1586
|
-
const client = await getClient();
|
|
1587
|
-
const detail = await client.getSessionArchive(
|
|
1588
|
-
session.ovSessionId,
|
|
1589
|
-
archiveId,
|
|
1590
|
-
session.agentId,
|
|
1591
|
-
);
|
|
1592
|
-
|
|
1593
|
-
const header = [
|
|
1594
|
-
`## ${detail.archive_id}`,
|
|
1595
|
-
detail.abstract ? `**Summary**: ${detail.abstract}` : "",
|
|
1596
|
-
`**Messages**: ${detail.messages.length}`,
|
|
1597
|
-
"",
|
|
1598
|
-
].filter(Boolean).join("\n");
|
|
1599
|
-
|
|
1600
|
-
const body = detail.messages
|
|
1601
|
-
.map((m: OVMessage) => formatMessageFaithful(m))
|
|
1602
|
-
.join("\n\n");
|
|
1603
|
-
|
|
1604
|
-
api.logger.info?.(`openviking: ov_archive_expand expanded ${detail.archive_id}, messages=${detail.messages.length}, chars=${body.length}, sessionId=${sessionId}`);
|
|
1605
|
-
return {
|
|
1606
|
-
content: [{ type: "text", text: `${header}\n${body}` }],
|
|
1607
|
-
details: {
|
|
1608
|
-
action: "expanded",
|
|
1609
|
-
archiveId: detail.archive_id,
|
|
1610
|
-
messageCount: detail.messages.length,
|
|
1611
|
-
sessionId,
|
|
1612
|
-
ovSessionId: session.ovSessionId,
|
|
1613
|
-
},
|
|
1614
|
-
};
|
|
1615
|
-
} catch (err) {
|
|
1616
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
1617
|
-
api.logger.warn?.(`openviking: ov_archive_expand failed (archiveId=${archiveId}, sessionId=${sessionId}): ${msg}`);
|
|
1618
|
-
return {
|
|
1619
|
-
content: [{ type: "text", text: `Failed to expand ${archiveId}: ${msg}` }],
|
|
1620
|
-
details: { error: msg, archiveId, sessionId, ovSessionId: session.ovSessionId },
|
|
1621
|
-
};
|
|
1622
|
-
}
|
|
1623
|
-
},
|
|
1624
|
-
}), { name: "ov_archive_expand" });
|
|
1625
|
-
|
|
1626
|
-
api.registerTool(
|
|
1627
|
-
(ctx: ToolContext) => ({
|
|
1628
|
-
name: "openviking_tool_result_read",
|
|
1629
|
-
label: "Tool Result Read (OpenViking)",
|
|
1630
|
-
description:
|
|
1631
|
-
"Restore the full original content of a tool result that was externalized by OpenViking. " +
|
|
1632
|
-
"Use when a previous tool result was externalized and only a preview is visible — " +
|
|
1633
|
-
"the preview contains a [tool-result-ref] or viking://session/.../tool-results/... URI. " +
|
|
1634
|
-
"\"Read\" tool returns the same truncated preview; this tool returns the complete content. " +
|
|
1635
|
-
"To read all content: pass offset=0 and a limit large enough to cover the whole result " +
|
|
1636
|
-
"(e.g. limit=100000). Use offset/limit for paging only when you need a specific section.",
|
|
1637
|
-
parameters: Type.Object({
|
|
1638
|
-
tool_output_ref: Type.String({
|
|
1639
|
-
description:
|
|
1640
|
-
"Exact OV URI from the preview, e.g. viking://session/<session_id>/tool-results/<tool_result_id>",
|
|
1641
|
-
}),
|
|
1642
|
-
offset: Type.Optional(Type.Number({ description: "Unicode character offset. Default: 0" })),
|
|
1643
|
-
limit: Type.Optional(Type.Number({ description: "Maximum Unicode characters to read. Default: 20000" })),
|
|
1644
|
-
}),
|
|
1645
|
-
async execute(_toolCallId: string, params: Record<string, unknown>) {
|
|
1646
|
-
if (isBypassedSession(ctx)) {
|
|
1647
|
-
return makeBypassedToolResult("openviking_tool_result_read");
|
|
1648
|
-
}
|
|
1649
|
-
const session = resolvePluginSessionRouting(ctx);
|
|
1650
|
-
if (!session.ovSessionId) {
|
|
1651
|
-
return {
|
|
1652
|
-
content: [{ type: "text", text: "Error: no active session." }],
|
|
1653
|
-
details: { error: "no_session" },
|
|
1654
|
-
};
|
|
1655
|
-
}
|
|
1656
|
-
|
|
1657
|
-
const parsed = parseToolResultRef(params.tool_output_ref ?? params.ref ?? params.uri);
|
|
1658
|
-
if (!parsed) {
|
|
1659
|
-
return {
|
|
1660
|
-
content: [{ type: "text", text: "Error: tool_output_ref must be a viking://session/.../tool-results/... URI." }],
|
|
1661
|
-
details: { error: "invalid_tool_output_ref" },
|
|
1662
|
-
};
|
|
1663
|
-
}
|
|
1664
|
-
if (parsed.sessionId !== session.ovSessionId) {
|
|
1665
|
-
return {
|
|
1666
|
-
content: [{ type: "text", text: "Error: refusing to read a tool result from another session." }],
|
|
1667
|
-
details: {
|
|
1668
|
-
error: "session_mismatch",
|
|
1669
|
-
requestedSessionId: parsed.sessionId,
|
|
1670
|
-
currentSessionId: session.ovSessionId,
|
|
1671
|
-
},
|
|
1672
|
-
};
|
|
1673
|
-
}
|
|
1674
|
-
|
|
1675
|
-
const offset = Math.max(0, getOptionalInteger(params.offset, 0));
|
|
1676
|
-
const limit = getOptionalInteger(params.limit, 20_000);
|
|
1677
|
-
if (limit < -1) {
|
|
1678
|
-
return {
|
|
1679
|
-
content: [{ type: "text", text: "Error: limit must be -1 or greater than or equal to 0." }],
|
|
1680
|
-
details: { error: "invalid_limit", limit },
|
|
1681
|
-
};
|
|
1682
|
-
}
|
|
232
|
+
const handleQueryConfigCommand = createOpenVikingQueryConfigCommandHandler({
|
|
233
|
+
resolvePluginSessionRouting,
|
|
234
|
+
toQueryConfigContext,
|
|
235
|
+
queryConfigStore,
|
|
236
|
+
normalizeRuntimeQueryParams,
|
|
237
|
+
});
|
|
1683
238
|
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
session.ovSessionId,
|
|
1688
|
-
parsed.toolResultId,
|
|
1689
|
-
{ offset, limit, includeMetadata: true },
|
|
1690
|
-
session.agentId,
|
|
1691
|
-
);
|
|
1692
|
-
const returnedChars = result.content.length;
|
|
1693
|
-
const nextOffset = result.offset + returnedChars;
|
|
1694
|
-
const text = result.content || "(empty tool result chunk)";
|
|
1695
|
-
return {
|
|
1696
|
-
content: [{ type: "text", text }],
|
|
1697
|
-
details: {
|
|
1698
|
-
action: "read",
|
|
1699
|
-
tool_output_ref: parsed.ref,
|
|
1700
|
-
tool_result_id: result.tool_result_id,
|
|
1701
|
-
offset: result.offset,
|
|
1702
|
-
limit: result.limit,
|
|
1703
|
-
returned_chars: returnedChars,
|
|
1704
|
-
total_chars: result.total_chars,
|
|
1705
|
-
has_more: result.has_more,
|
|
1706
|
-
next_offset: result.has_more ? nextOffset : null,
|
|
1707
|
-
metadata: result.metadata ?? null,
|
|
1708
|
-
},
|
|
1709
|
-
};
|
|
1710
|
-
} catch (err) {
|
|
1711
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
1712
|
-
api.logger.warn?.(`openviking: openviking_tool_result_read failed: ${msg}`);
|
|
1713
|
-
return {
|
|
1714
|
-
content: [{ type: "text", text: `Failed to read tool result: ${msg}` }],
|
|
1715
|
-
details: { error: msg, tool_output_ref: parsed.ref },
|
|
1716
|
-
};
|
|
1717
|
-
}
|
|
1718
|
-
},
|
|
1719
|
-
}),
|
|
1720
|
-
{ name: "openviking_tool_result_read" },
|
|
1721
|
-
);
|
|
239
|
+
const { addResourceOpenViking, addSkillOpenViking } = createOpenVikingImportRuntime({
|
|
240
|
+
getClient,
|
|
241
|
+
});
|
|
1722
242
|
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
};
|
|
1751
|
-
}
|
|
243
|
+
const queryRuntime = createOpenVikingQueryRuntime({
|
|
244
|
+
getClient,
|
|
245
|
+
queryConfigStore,
|
|
246
|
+
toQueryConfigContext,
|
|
247
|
+
traceRecorder,
|
|
248
|
+
inferRecallResourceType,
|
|
249
|
+
createTraceId,
|
|
250
|
+
boundTraceQuery,
|
|
251
|
+
previewText,
|
|
252
|
+
logger: api.logger,
|
|
253
|
+
cfg,
|
|
254
|
+
});
|
|
255
|
+
const {
|
|
256
|
+
searchOpenViking,
|
|
257
|
+
readOpenVikingContent,
|
|
258
|
+
multiReadOpenVikingContent,
|
|
259
|
+
listOpenVikingDirectory,
|
|
260
|
+
} = queryRuntime;
|
|
261
|
+
|
|
262
|
+
registerOpenVikingImportTools({
|
|
263
|
+
registerTool: registerOpenVikingTool,
|
|
264
|
+
getClient,
|
|
265
|
+
resolvePluginSessionRouting,
|
|
266
|
+
isBypassedSession,
|
|
267
|
+
makeBypassedToolResult,
|
|
268
|
+
enableAddResourceTool: cfg.enableAddResourceTool,
|
|
269
|
+
});
|
|
1752
270
|
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
details: {
|
|
1764
|
-
error: "session_mismatch",
|
|
1765
|
-
requestedSessionId: parsed.sessionId,
|
|
1766
|
-
currentSessionId: session.ovSessionId,
|
|
1767
|
-
},
|
|
1768
|
-
};
|
|
1769
|
-
}
|
|
271
|
+
registerOpenVikingQueryTools({
|
|
272
|
+
registerTool: registerOpenVikingTool,
|
|
273
|
+
searchOpenViking,
|
|
274
|
+
readOpenVikingContent,
|
|
275
|
+
multiReadOpenVikingContent,
|
|
276
|
+
listOpenVikingDirectory,
|
|
277
|
+
resolvePluginSessionRouting,
|
|
278
|
+
isBypassedSession,
|
|
279
|
+
makeBypassedToolResult,
|
|
280
|
+
});
|
|
1770
281
|
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
282
|
+
registerOpenVikingMemoryRecallTools({
|
|
283
|
+
registerTool: registerOpenVikingTool,
|
|
284
|
+
getClient,
|
|
285
|
+
queryConfigStore,
|
|
286
|
+
toQueryConfigContext,
|
|
287
|
+
resolvePluginSessionRouting,
|
|
288
|
+
isBypassedSession,
|
|
289
|
+
makeBypassedToolResult,
|
|
290
|
+
resolveRecallSearchPlan,
|
|
291
|
+
postProcessMemories,
|
|
292
|
+
pickMemoriesForInjection,
|
|
293
|
+
buildMemoryLinesWithBudget,
|
|
294
|
+
inferRecallResourceType,
|
|
295
|
+
createTraceId,
|
|
296
|
+
boundTraceQuery,
|
|
297
|
+
previewText,
|
|
298
|
+
traceRecorder,
|
|
299
|
+
cfg,
|
|
300
|
+
logger: api.logger,
|
|
301
|
+
});
|
|
1783
302
|
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
);
|
|
1793
|
-
const matches = result.matches ?? [];
|
|
1794
|
-
const text = matches.length
|
|
1795
|
-
? [
|
|
1796
|
-
`Found ${matches.length} match(es) for "${query}" in ${parsed.ref}:`,
|
|
1797
|
-
"",
|
|
1798
|
-
...matches.map((match, index) =>
|
|
1799
|
-
`## Match ${index + 1} (offset ${match.offset})\n${match.snippet}`,
|
|
1800
|
-
),
|
|
1801
|
-
].join("\n")
|
|
1802
|
-
: `No matches found for "${query}" in ${parsed.ref}.`;
|
|
1803
|
-
return {
|
|
1804
|
-
content: [{ type: "text", text }],
|
|
1805
|
-
details: {
|
|
1806
|
-
action: "searched",
|
|
1807
|
-
tool_output_ref: parsed.ref,
|
|
1808
|
-
tool_result_id: result.tool_result_id,
|
|
1809
|
-
query,
|
|
1810
|
-
match_count: matches.length,
|
|
1811
|
-
matches,
|
|
1812
|
-
},
|
|
1813
|
-
};
|
|
1814
|
-
} catch (err) {
|
|
1815
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
1816
|
-
api.logger.warn?.(`openviking: openviking_tool_result_search failed: ${msg}`);
|
|
1817
|
-
return {
|
|
1818
|
-
content: [{ type: "text", text: `Failed to search tool result: ${msg}` }],
|
|
1819
|
-
details: { error: msg, tool_output_ref: parsed.ref, query },
|
|
1820
|
-
};
|
|
1821
|
-
}
|
|
1822
|
-
},
|
|
1823
|
-
}),
|
|
1824
|
-
{ name: "openviking_tool_result_search" },
|
|
1825
|
-
);
|
|
303
|
+
registerOpenVikingRecallTraceTools({
|
|
304
|
+
registerTool: registerOpenVikingTool,
|
|
305
|
+
queryRecallTraces,
|
|
306
|
+
formatRecallTraceText,
|
|
307
|
+
resolvePluginSessionRouting,
|
|
308
|
+
isBypassedSession,
|
|
309
|
+
makeBypassedToolResult,
|
|
310
|
+
});
|
|
1826
311
|
|
|
1827
|
-
api
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
312
|
+
registerOpenVikingCommands(api, createOpenVikingCommandDefinitions({
|
|
313
|
+
resolvePluginSessionRouting,
|
|
314
|
+
isBypassedSession,
|
|
315
|
+
makeBypassedToolResult,
|
|
316
|
+
parseAddResourceCommandArgs,
|
|
317
|
+
parseAddSkillCommandArgs,
|
|
318
|
+
parseOVSearchCommandArgs,
|
|
319
|
+
addResourceOpenViking,
|
|
320
|
+
addSkillOpenViking,
|
|
321
|
+
searchOpenViking,
|
|
322
|
+
handleQueryConfigCommand,
|
|
323
|
+
queryRecallTraces,
|
|
324
|
+
formatRecallTraceText,
|
|
325
|
+
}));
|
|
326
|
+
|
|
327
|
+
registerOpenVikingMemoryTools({
|
|
328
|
+
registerTool: registerOpenVikingTool,
|
|
329
|
+
getClient,
|
|
330
|
+
normalizeSessionId: openClawSessionRefToOvStorageId,
|
|
331
|
+
createTempSessionId: createMemoryStoreTempSessionId,
|
|
332
|
+
extractSenderId: extractToolSenderId,
|
|
333
|
+
toRoleId,
|
|
334
|
+
resolvePluginSessionRouting,
|
|
335
|
+
isBypassedSession,
|
|
336
|
+
makeBypassedToolResult,
|
|
337
|
+
defaultTargetUri: cfg.targetUri,
|
|
338
|
+
defaultRecallScoreThreshold: cfg.recallScoreThreshold,
|
|
339
|
+
logFindRequests: cfg.logFindRequests,
|
|
340
|
+
logger: api.logger,
|
|
341
|
+
});
|
|
1850
342
|
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
343
|
+
registerOpenVikingArchiveTools({
|
|
344
|
+
registerTool: registerOpenVikingTool,
|
|
345
|
+
getClient,
|
|
346
|
+
rememberSessionAgentId,
|
|
347
|
+
toOvSessionId: openClawSessionToOvStorageId,
|
|
348
|
+
resolveAgentId,
|
|
349
|
+
resolvePluginSessionRouting,
|
|
350
|
+
isBypassedSession,
|
|
351
|
+
makeBypassedToolResult,
|
|
352
|
+
formatMessage: formatMessageFaithful,
|
|
353
|
+
traceRecorder,
|
|
354
|
+
traceRecallMaxResultsPerSearch: cfg.traceRecallMaxResultsPerSearch,
|
|
355
|
+
traceRecallPreviewChars: cfg.traceRecallPreviewChars,
|
|
356
|
+
createTraceId,
|
|
357
|
+
logger: api.logger,
|
|
358
|
+
});
|
|
1858
359
|
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
const text = items.length
|
|
1868
|
-
? [
|
|
1869
|
-
`Found ${items.length} externalized tool result(s) in current session:`,
|
|
1870
|
-
"",
|
|
1871
|
-
...items.map((item, index) => {
|
|
1872
|
-
const ref = typeof item.storage_uri === "string" ? item.storage_uri : "(missing ref)";
|
|
1873
|
-
const name = typeof item.tool_name === "string" ? item.tool_name : "tool";
|
|
1874
|
-
const chars = typeof item.original_chars === "number" ? item.original_chars : "unknown";
|
|
1875
|
-
const created = typeof item.created_at === "string" ? ` created_at=${item.created_at}` : "";
|
|
1876
|
-
return `${index + 1}. ${name} original_chars=${chars}${created}\nref: ${ref}`;
|
|
1877
|
-
}),
|
|
1878
|
-
].join("\n")
|
|
1879
|
-
: toolName
|
|
1880
|
-
? `No externalized tool results found for tool "${toolName}" in current session.`
|
|
1881
|
-
: "No externalized tool results found in current session.";
|
|
1882
|
-
return {
|
|
1883
|
-
content: [{ type: "text", text }],
|
|
1884
|
-
details: {
|
|
1885
|
-
action: "listed",
|
|
1886
|
-
session_id: session.ovSessionId,
|
|
1887
|
-
tool_name: toolName ?? null,
|
|
1888
|
-
count: items.length,
|
|
1889
|
-
tool_results: items,
|
|
1890
|
-
},
|
|
1891
|
-
};
|
|
1892
|
-
} catch (err) {
|
|
1893
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
1894
|
-
api.logger.warn?.(`openviking: openviking_tool_result_list failed: ${msg}`);
|
|
1895
|
-
return {
|
|
1896
|
-
content: [{ type: "text", text: `Failed to list tool results: ${msg}` }],
|
|
1897
|
-
details: { error: msg, session_id: session.ovSessionId, tool_name: toolName ?? null },
|
|
1898
|
-
};
|
|
1899
|
-
}
|
|
1900
|
-
},
|
|
1901
|
-
}),
|
|
1902
|
-
{ name: "openviking_tool_result_list" },
|
|
1903
|
-
);
|
|
360
|
+
registerOpenVikingToolResultTools({
|
|
361
|
+
registerTool: registerOpenVikingTool,
|
|
362
|
+
getClient,
|
|
363
|
+
resolvePluginSessionRouting,
|
|
364
|
+
isBypassedSession,
|
|
365
|
+
makeBypassedToolResult,
|
|
366
|
+
logger: api.logger,
|
|
367
|
+
});
|
|
1904
368
|
|
|
1905
|
-
|
|
1906
|
-
const sessionAgentResolver = createSessionAgentResolver(cfg.agent_prefix);
|
|
1907
|
-
const rememberSessionAgentId = (ctx: SessionAgentLookup) => {
|
|
1908
|
-
sessionAgentResolver.remember(ctx);
|
|
1909
|
-
};
|
|
1910
|
-
const resolveAgentId = (
|
|
1911
|
-
sessionId?: string,
|
|
1912
|
-
sessionKey?: string,
|
|
1913
|
-
ovSessionId?: string,
|
|
1914
|
-
): string => {
|
|
1915
|
-
const sid = typeof sessionId === "string" ? sessionId.trim() : "";
|
|
1916
|
-
const sk = typeof sessionKey === "string" ? sessionKey.trim() : "";
|
|
1917
|
-
const ovSid = typeof ovSessionId === "string" ? ovSessionId.trim() : "";
|
|
1918
|
-
const result = sessionAgentResolver.resolve(sid, sk, ovSid);
|
|
1919
|
-
if (cfg.logFindRequests) {
|
|
1920
|
-
api.logger.info(
|
|
1921
|
-
`openviking: resolveAgentId ${JSON.stringify({
|
|
1922
|
-
sessionId: sid || "(empty)",
|
|
1923
|
-
sessionKey: sk || "(empty)",
|
|
1924
|
-
ovSessionId: ovSid || "(empty)",
|
|
1925
|
-
parsedConfigAgentPrefix: cfg.agent_prefix,
|
|
1926
|
-
mappedResolvedAgentId: result.mappedResolvedAgentId,
|
|
1927
|
-
resolvedBeforeSanitize: result.resolvedBeforeSanitize,
|
|
1928
|
-
resolved: result.resolved,
|
|
1929
|
-
branch: result.branch,
|
|
1930
|
-
aliases: result.aliases,
|
|
1931
|
-
fromExplicitBinding: result.fromExplicitBinding,
|
|
1932
|
-
})}`,
|
|
1933
|
-
);
|
|
1934
|
-
}
|
|
1935
|
-
return result.resolved;
|
|
1936
|
-
};
|
|
369
|
+
const { getContextEngine, setContextEngineRef } = createOpenVikingContextEngineRef();
|
|
1937
370
|
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
if (isBypassedSession(ctx)) {
|
|
1946
|
-
verboseRoutingInfo(
|
|
1947
|
-
`openviking: bypassing before_reset due to session pattern match (sessionKey=${ctx?.sessionKey ?? "none"}, sessionId=${ctx?.sessionId ?? "none"})`,
|
|
1948
|
-
);
|
|
1949
|
-
return;
|
|
1950
|
-
}
|
|
1951
|
-
const sessionId = ctx?.sessionId;
|
|
1952
|
-
if (sessionId && contextEngineRef) {
|
|
1953
|
-
try {
|
|
1954
|
-
const ok = await contextEngineRef.commitOVSession({
|
|
1955
|
-
sessionId,
|
|
1956
|
-
sessionKey: ctx?.sessionKey,
|
|
1957
|
-
});
|
|
1958
|
-
if (ok) {
|
|
1959
|
-
api.logger.info(`openviking: committed OV session on reset for session=${sessionId}`);
|
|
1960
|
-
}
|
|
1961
|
-
} catch (err) {
|
|
1962
|
-
api.logger.warn(`openviking: failed to commit OV session on reset: ${String(err)}`);
|
|
1963
|
-
}
|
|
1964
|
-
}
|
|
1965
|
-
});
|
|
1966
|
-
api.on("after_compaction", async (_event: unknown, _ctx?: HookAgentContext) => {
|
|
1967
|
-
// Reserved hook registration for future post-compaction memory integration.
|
|
371
|
+
registerOpenVikingLifecycleHooks({
|
|
372
|
+
api,
|
|
373
|
+
rememberSessionAgentId,
|
|
374
|
+
isBypassedSession,
|
|
375
|
+
verboseRoutingInfo,
|
|
376
|
+
getContextEngine,
|
|
377
|
+
logger: api.logger,
|
|
1968
378
|
});
|
|
1969
379
|
|
|
1970
|
-
|
|
1971
|
-
api
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
api.logger.info(
|
|
1985
|
-
"openviking: registered context-engine (assemble=archive+active+auto-recall, afterTurn=auto-capture, session→OV id=uuid-or-sha256 + diag/Phase2 options)",
|
|
1986
|
-
);
|
|
1987
|
-
} else {
|
|
1988
|
-
api.logger.warn(
|
|
1989
|
-
"openviking: registerContextEngine is unavailable; context-engine behavior will not run",
|
|
1990
|
-
);
|
|
1991
|
-
}
|
|
380
|
+
registerOpenVikingContextEngine({
|
|
381
|
+
api,
|
|
382
|
+
plugin: contextEnginePlugin,
|
|
383
|
+
version: "0.1.0",
|
|
384
|
+
cfg,
|
|
385
|
+
logger: api.logger,
|
|
386
|
+
getClient,
|
|
387
|
+
resolveAgentId,
|
|
388
|
+
rememberSessionAgentId,
|
|
389
|
+
queryConfigStore,
|
|
390
|
+
traceRecorder,
|
|
391
|
+
createContextEngine: createMemoryOpenVikingContextEngine,
|
|
392
|
+
setContextEngineRef,
|
|
393
|
+
});
|
|
1992
394
|
|
|
1993
395
|
registerSetupCli(api);
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
stop: () => {
|
|
2004
|
-
api.logger.info("openviking: stopped");
|
|
2005
|
-
},
|
|
2006
|
-
});
|
|
396
|
+
const recallTraceHttpRoutesRegistered = registerRecallTraceRoutes(api);
|
|
397
|
+
|
|
398
|
+
api.registerService(createOpenVikingService({
|
|
399
|
+
cfg,
|
|
400
|
+
getClient,
|
|
401
|
+
logger: api.logger,
|
|
402
|
+
recallTraceHttpRoutesRegistered,
|
|
403
|
+
registerRecallTraceRoutes,
|
|
404
|
+
}));
|
|
2007
405
|
},
|
|
2008
406
|
};
|
|
2009
407
|
|