@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.
Files changed (108) hide show
  1. package/INSTALL-AGENT.md +4 -4
  2. package/INSTALL-ZH.md +68 -8
  3. package/INSTALL.md +12 -8
  4. package/README.md +45 -35
  5. package/adapters/http-transport.ts +3 -0
  6. package/adapters/resource-packager.ts +141 -0
  7. package/auto-recall.ts +258 -27
  8. package/client.ts +201 -177
  9. package/commands/setup.ts +200 -33
  10. package/config/feature-gates.json +10 -0
  11. package/config.ts +428 -85
  12. package/context-engine.ts +71 -1402
  13. package/dist/adapters/http-transport.js +1 -0
  14. package/dist/adapters/resource-packager.js +114 -0
  15. package/dist/auto-recall.js +201 -25
  16. package/dist/client.js +125 -161
  17. package/dist/commands/setup.js +170 -32
  18. package/dist/config.js +285 -64
  19. package/dist/context-engine.js +62 -1173
  20. package/dist/index.js +209 -1489
  21. package/dist/memory-ranking.js +35 -12
  22. package/dist/plugin/command-registration.js +5 -0
  23. package/dist/plugin/openviking-archive-tools.js +210 -0
  24. package/dist/plugin/openviking-bypass-runtime.js +8 -0
  25. package/dist/plugin/openviking-client-runtime.js +28 -0
  26. package/dist/plugin/openviking-command-args.js +146 -0
  27. package/dist/plugin/openviking-command-definitions.js +206 -0
  28. package/dist/plugin/openviking-context-engine-ref.js +11 -0
  29. package/dist/plugin/openviking-context-engine-registration.js +23 -0
  30. package/dist/plugin/openviking-feature-gates.js +250 -0
  31. package/dist/plugin/openviking-import-runtime.js +50 -0
  32. package/dist/plugin/openviking-import-tools.js +86 -0
  33. package/dist/plugin/openviking-lifecycle-hooks.js +33 -0
  34. package/dist/plugin/openviking-memory-recall-tools.js +233 -0
  35. package/dist/plugin/openviking-memory-tools.js +199 -0
  36. package/dist/plugin/openviking-query-config-command.js +203 -0
  37. package/dist/plugin/openviking-query-runtime.js +367 -0
  38. package/dist/plugin/openviking-query-tools.js +91 -0
  39. package/dist/plugin/openviking-recall-trace-runtime.js +387 -0
  40. package/dist/plugin/openviking-recall-trace-tools.js +38 -0
  41. package/dist/plugin/openviking-runtime-state.js +26 -0
  42. package/dist/plugin/openviking-runtime-utils.js +62 -0
  43. package/dist/plugin/openviking-services.js +20 -0
  44. package/dist/plugin/openviking-session-routing-runtime.js +61 -0
  45. package/dist/plugin/openviking-tool-result-tools.js +268 -0
  46. package/dist/plugin/recall-trace-routes.js +77 -0
  47. package/dist/plugin/tool-registration.js +23 -0
  48. package/dist/process-manager.js +1 -1
  49. package/dist/query-config.js +336 -0
  50. package/dist/recall-trace.js +309 -0
  51. package/dist/registries/recall-resource-types.js +58 -0
  52. package/dist/routing/identity-routing.js +152 -0
  53. package/dist/routing/memory-uri.js +7 -0
  54. package/dist/services/context-lifecycle-service.js +847 -0
  55. package/dist/services/context-message-adapter.js +340 -0
  56. package/dist/session-transcript-repair.js +0 -1
  57. package/dist/text-utils.js +14 -31
  58. package/index.ts +271 -1873
  59. package/install-manifest.json +10 -1
  60. package/memory-ranking.ts +46 -10
  61. package/openclaw.plugin.json +198 -36
  62. package/package.json +14 -4
  63. package/plugin/command-registration.ts +34 -0
  64. package/plugin/openviking-archive-tools.ts +301 -0
  65. package/plugin/openviking-bypass-runtime.ts +26 -0
  66. package/plugin/openviking-client-runtime.ts +74 -0
  67. package/plugin/openviking-command-args.ts +187 -0
  68. package/plugin/openviking-command-definitions.ts +294 -0
  69. package/plugin/openviking-context-engine-ref.ts +16 -0
  70. package/plugin/openviking-context-engine-registration.ts +97 -0
  71. package/plugin/openviking-feature-gates.ts +335 -0
  72. package/plugin/openviking-import-runtime.ts +105 -0
  73. package/plugin/openviking-import-tools.ts +133 -0
  74. package/plugin/openviking-lifecycle-hooks.ts +65 -0
  75. package/plugin/openviking-memory-recall-tools.ts +368 -0
  76. package/plugin/openviking-memory-tools.ts +299 -0
  77. package/plugin/openviking-query-config-command.ts +230 -0
  78. package/plugin/openviking-query-formatters.ts +108 -0
  79. package/plugin/openviking-query-runtime.ts +476 -0
  80. package/plugin/openviking-query-tools.ts +161 -0
  81. package/plugin/openviking-recall-trace-runtime.ts +482 -0
  82. package/plugin/openviking-recall-trace-tools.ts +99 -0
  83. package/plugin/openviking-runtime-state.ts +52 -0
  84. package/plugin/openviking-runtime-utils.ts +67 -0
  85. package/plugin/openviking-services.ts +49 -0
  86. package/plugin/openviking-session-routing-runtime.ts +105 -0
  87. package/plugin/openviking-tool-result-tools.ts +397 -0
  88. package/plugin/recall-trace-routes.ts +133 -0
  89. package/plugin/tool-registration.ts +51 -0
  90. package/process-manager.ts +1 -1
  91. package/query-config.ts +400 -0
  92. package/recall-trace.ts +442 -0
  93. package/registries/openviking-tools.ts +84 -0
  94. package/registries/recall-resource-types.ts +74 -0
  95. package/routing/identity-routing.ts +210 -0
  96. package/routing/memory-uri.ts +8 -0
  97. package/services/context-lifecycle-service.ts +1209 -0
  98. package/services/context-message-adapter.ts +375 -0
  99. package/services/setup/config-writer.ts +125 -0
  100. package/services/setup/exit-utils.ts +5 -0
  101. package/services/setup/package-metadata.ts +56 -0
  102. package/services/setup/probe-service.ts +143 -0
  103. package/services/setup/setup-flow.ts +306 -0
  104. package/services/setup/version-compatibility.ts +41 -0
  105. package/session-transcript-repair.ts +0 -2
  106. package/skills/install-openviking-memory/SKILL.md +123 -50
  107. package/skills/openviking-context-database/SKILL.md +286 -0
  108. package/text-utils.ts +15 -34
@@ -0,0 +1,268 @@
1
+ import { Type } from "@sinclair/typebox";
2
+ export function parseToolResultRef(value) {
3
+ const raw = typeof value === "string" ? value.trim() : "";
4
+ if (!raw) {
5
+ return null;
6
+ }
7
+ const match = raw.match(/^viking:\/\/session\/([^/]+)\/tool-results\/([^/?#]+)(?:[?#].*)?$/);
8
+ if (!match) {
9
+ return null;
10
+ }
11
+ const sessionId = decodeURIComponent(match[1]);
12
+ const toolResultId = decodeURIComponent(match[2]);
13
+ if (!sessionId || !toolResultId) {
14
+ return null;
15
+ }
16
+ return {
17
+ sessionId,
18
+ toolResultId,
19
+ ref: `viking://session/${encodeURIComponent(sessionId)}/tool-results/${encodeURIComponent(toolResultId)}`,
20
+ };
21
+ }
22
+ function getOptionalInteger(value, fallback) {
23
+ if (typeof value !== "number" || !Number.isFinite(value)) {
24
+ return fallback;
25
+ }
26
+ return Math.floor(value);
27
+ }
28
+ function getPositiveInteger(value, fallback) {
29
+ return Math.max(1, getOptionalInteger(value, fallback));
30
+ }
31
+ export function registerOpenVikingToolResultTools({ registerTool, getClient, resolvePluginSessionRouting, isBypassedSession, makeBypassedToolResult, logger, }) {
32
+ registerTool((ctx) => ({
33
+ name: "openviking_tool_result_read",
34
+ label: "Tool Result Read (OpenViking)",
35
+ description: "Restore the full original content of a tool result that was externalized by OpenViking. " +
36
+ "Use when a previous tool result was externalized and only a preview is visible — " +
37
+ "the preview contains a [tool-result-ref] or viking://session/.../tool-results/... URI. " +
38
+ "\"Read\" tool returns the same truncated preview; this tool returns the complete content. " +
39
+ "To read all content: pass offset=0 and a limit large enough to cover the whole result " +
40
+ "(e.g. limit=100000). Use offset/limit for paging only when you need a specific section.",
41
+ parameters: Type.Object({
42
+ tool_output_ref: Type.String({
43
+ description: "Exact OV URI from the preview, e.g. viking://session/<session_id>/tool-results/<tool_result_id>",
44
+ }),
45
+ offset: Type.Optional(Type.Number({ description: "Unicode character offset. Default: 0" })),
46
+ limit: Type.Optional(Type.Number({ description: "Maximum Unicode characters to read. Default: 20000" })),
47
+ }),
48
+ async execute(_toolCallId, params) {
49
+ if (isBypassedSession(ctx)) {
50
+ return makeBypassedToolResult("openviking_tool_result_read");
51
+ }
52
+ const session = resolvePluginSessionRouting(ctx);
53
+ if (!session.ovSessionId) {
54
+ return {
55
+ content: [{ type: "text", text: "Error: no active session." }],
56
+ details: { error: "no_session" },
57
+ };
58
+ }
59
+ const parsed = parseToolResultRef(params.tool_output_ref ?? params.ref ?? params.uri);
60
+ if (!parsed) {
61
+ return {
62
+ content: [{ type: "text", text: "Error: tool_output_ref must be a viking://session/.../tool-results/... URI." }],
63
+ details: { error: "invalid_tool_output_ref" },
64
+ };
65
+ }
66
+ if (parsed.sessionId !== session.ovSessionId) {
67
+ return {
68
+ content: [{ type: "text", text: "Error: refusing to read a tool result from another session." }],
69
+ details: {
70
+ error: "session_mismatch",
71
+ requestedSessionId: parsed.sessionId,
72
+ currentSessionId: session.ovSessionId,
73
+ },
74
+ };
75
+ }
76
+ const offset = Math.max(0, getOptionalInteger(params.offset, 0));
77
+ const limit = getOptionalInteger(params.limit, 20_000);
78
+ if (limit < -1) {
79
+ return {
80
+ content: [{ type: "text", text: "Error: limit must be -1 or greater than or equal to 0." }],
81
+ details: { error: "invalid_limit", limit },
82
+ };
83
+ }
84
+ try {
85
+ const client = await getClient();
86
+ const result = await client.readToolResult(session.ovSessionId, parsed.toolResultId, { offset, limit, includeMetadata: true }, session.agentId);
87
+ const returnedChars = result.content.length;
88
+ const nextOffset = result.offset + returnedChars;
89
+ const text = result.content || "(empty tool result chunk)";
90
+ return {
91
+ content: [{ type: "text", text }],
92
+ details: {
93
+ action: "read",
94
+ tool_output_ref: parsed.ref,
95
+ tool_result_id: result.tool_result_id,
96
+ offset: result.offset,
97
+ limit: result.limit,
98
+ returned_chars: returnedChars,
99
+ total_chars: result.total_chars,
100
+ has_more: result.has_more,
101
+ next_offset: result.has_more ? nextOffset : null,
102
+ metadata: result.metadata ?? null,
103
+ },
104
+ };
105
+ }
106
+ catch (err) {
107
+ const msg = err instanceof Error ? err.message : String(err);
108
+ logger?.warn?.(`openviking: openviking_tool_result_read failed: ${msg}`);
109
+ return {
110
+ content: [{ type: "text", text: `Failed to read tool result: ${msg}` }],
111
+ details: { error: msg, tool_output_ref: parsed.ref },
112
+ };
113
+ }
114
+ },
115
+ }), { name: "openviking_tool_result_read" });
116
+ registerTool((ctx) => ({
117
+ name: "openviking_tool_result_search",
118
+ label: "Tool Result Search (OpenViking)",
119
+ description: "Search inside an externalized tool result for a keyword. " +
120
+ "Use when you need to find specific content in a large externalized result, " +
121
+ "before reading it with openviking_tool_result_read. " +
122
+ "Returns matching snippets with their character offsets.",
123
+ parameters: Type.Object({
124
+ tool_output_ref: Type.String({
125
+ description: "Exact OV URI from the preview, e.g. viking://session/<session_id>/tool-results/<tool_result_id>",
126
+ }),
127
+ query: Type.String({ description: "Keyword or exact text to search for" }),
128
+ limit: Type.Optional(Type.Number({ description: "Maximum matches. Default: 20" })),
129
+ context_chars: Type.Optional(Type.Number({ description: "Characters around each match. Default: 300" })),
130
+ }),
131
+ async execute(_toolCallId, params) {
132
+ if (isBypassedSession(ctx)) {
133
+ return makeBypassedToolResult("openviking_tool_result_search");
134
+ }
135
+ const session = resolvePluginSessionRouting(ctx);
136
+ if (!session.ovSessionId) {
137
+ return {
138
+ content: [{ type: "text", text: "Error: no active session." }],
139
+ details: { error: "no_session" },
140
+ };
141
+ }
142
+ const parsed = parseToolResultRef(params.tool_output_ref ?? params.ref ?? params.uri);
143
+ if (!parsed) {
144
+ return {
145
+ content: [{ type: "text", text: "Error: tool_output_ref must be a viking://session/.../tool-results/... URI." }],
146
+ details: { error: "invalid_tool_output_ref" },
147
+ };
148
+ }
149
+ if (parsed.sessionId !== session.ovSessionId) {
150
+ return {
151
+ content: [{ type: "text", text: "Error: refusing to search a tool result from another session." }],
152
+ details: {
153
+ error: "session_mismatch",
154
+ requestedSessionId: parsed.sessionId,
155
+ currentSessionId: session.ovSessionId,
156
+ },
157
+ };
158
+ }
159
+ const query = String(params.query ?? "").trim();
160
+ if (!query) {
161
+ return {
162
+ content: [{ type: "text", text: "Error: query is required." }],
163
+ details: { error: "missing_param", param: "query" },
164
+ };
165
+ }
166
+ const limit = getPositiveInteger(params.limit, 20);
167
+ const contextChars = Math.max(0, getOptionalInteger(params.context_chars ?? params.contextChars, 300));
168
+ try {
169
+ const client = await getClient();
170
+ const result = await client.searchToolResult(session.ovSessionId, parsed.toolResultId, query, { limit, contextChars }, session.agentId);
171
+ const matches = result.matches ?? [];
172
+ const text = matches.length
173
+ ? [
174
+ `Found ${matches.length} match(es) for "${query}" in ${parsed.ref}:`,
175
+ "",
176
+ ...matches.map((match, index) => `## Match ${index + 1} (offset ${match.offset})\n${match.snippet}`),
177
+ ].join("\n")
178
+ : `No matches found for "${query}" in ${parsed.ref}.`;
179
+ return {
180
+ content: [{ type: "text", text }],
181
+ details: {
182
+ action: "searched",
183
+ tool_output_ref: parsed.ref,
184
+ tool_result_id: result.tool_result_id,
185
+ query,
186
+ match_count: matches.length,
187
+ matches,
188
+ },
189
+ };
190
+ }
191
+ catch (err) {
192
+ const msg = err instanceof Error ? err.message : String(err);
193
+ logger?.warn?.(`openviking: openviking_tool_result_search failed: ${msg}`);
194
+ return {
195
+ content: [{ type: "text", text: `Failed to search tool result: ${msg}` }],
196
+ details: { error: msg, tool_output_ref: parsed.ref, query },
197
+ };
198
+ }
199
+ },
200
+ }), { name: "openviking_tool_result_search" });
201
+ registerTool((ctx) => ({
202
+ name: "openviking_tool_result_list",
203
+ label: "Tool Result List (OpenViking)",
204
+ description: "List externalized tool results for the current session. " +
205
+ "Use to discover available refs before calling openviking_tool_result_read. " +
206
+ "Optionally filter by tool_name to narrow down results.",
207
+ parameters: Type.Object({
208
+ tool_name: Type.Optional(Type.String({ description: "Optional exact tool name filter" })),
209
+ limit: Type.Optional(Type.Number({ description: "Maximum results. Default: 50" })),
210
+ }),
211
+ async execute(_toolCallId, params) {
212
+ if (isBypassedSession(ctx)) {
213
+ return makeBypassedToolResult("openviking_tool_result_list");
214
+ }
215
+ const session = resolvePluginSessionRouting(ctx);
216
+ if (!session.ovSessionId) {
217
+ return {
218
+ content: [{ type: "text", text: "Error: no active session." }],
219
+ details: { error: "no_session" },
220
+ };
221
+ }
222
+ const toolName = typeof params.tool_name === "string" && params.tool_name.trim()
223
+ ? params.tool_name.trim()
224
+ : typeof params.toolName === "string" && params.toolName.trim()
225
+ ? params.toolName.trim()
226
+ : undefined;
227
+ const limit = getPositiveInteger(params.limit, 50);
228
+ try {
229
+ const client = await getClient();
230
+ const result = await client.listToolResults(session.ovSessionId, { toolName, limit }, session.agentId);
231
+ const items = result.tool_results ?? [];
232
+ const text = items.length
233
+ ? [
234
+ `Found ${items.length} externalized tool result(s) in current session:`,
235
+ "",
236
+ ...items.map((item, index) => {
237
+ const ref = typeof item.storage_uri === "string" ? item.storage_uri : "(missing ref)";
238
+ const name = typeof item.tool_name === "string" ? item.tool_name : "tool";
239
+ const chars = typeof item.original_chars === "number" ? item.original_chars : "unknown";
240
+ const created = typeof item.created_at === "string" ? ` created_at=${item.created_at}` : "";
241
+ return `${index + 1}. ${name} original_chars=${chars}${created}\nref: ${ref}`;
242
+ }),
243
+ ].join("\n")
244
+ : toolName
245
+ ? `No externalized tool results found for tool "${toolName}" in current session.`
246
+ : "No externalized tool results found in current session.";
247
+ return {
248
+ content: [{ type: "text", text }],
249
+ details: {
250
+ action: "listed",
251
+ session_id: session.ovSessionId,
252
+ tool_name: toolName ?? null,
253
+ count: items.length,
254
+ tool_results: items,
255
+ },
256
+ };
257
+ }
258
+ catch (err) {
259
+ const msg = err instanceof Error ? err.message : String(err);
260
+ logger?.warn?.(`openviking: openviking_tool_result_list failed: ${msg}`);
261
+ return {
262
+ content: [{ type: "text", text: `Failed to list tool results: ${msg}` }],
263
+ details: { error: msg, session_id: session.ovSessionId, tool_name: toolName ?? null },
264
+ };
265
+ }
266
+ },
267
+ }), { name: "openviking_tool_result_list" });
268
+ }
@@ -0,0 +1,77 @@
1
+ export const RECALL_TRACE_ROUTE_PATHS = [
2
+ "/api/openviking/recall-traces",
3
+ "/api/openviking/uri-detail",
4
+ "/api/openviking/recall-traces/latest-ov-search-list",
5
+ "/api/openviking/recall-traces/:traceId",
6
+ ];
7
+ export function registerRecallTraceRoutes(ctx, handlers) {
8
+ const routeAdapter = ctx;
9
+ const canRegisterLegacyRoute = typeof routeAdapter?.registerRoute === "function";
10
+ const canRegisterHttpRoute = typeof routeAdapter?.registerHttpRoute === "function";
11
+ if (!canRegisterLegacyRoute && !canRegisterHttpRoute) {
12
+ return false;
13
+ }
14
+ const handle = handlers.handleRecallTraces;
15
+ const routes = [
16
+ { method: "GET", path: RECALL_TRACE_ROUTE_PATHS[0], handler: handle },
17
+ { method: "GET", path: RECALL_TRACE_ROUTE_PATHS[1], handler: handlers.handleUriDetail },
18
+ { method: "GET", path: RECALL_TRACE_ROUTE_PATHS[2], handler: handlers.handleLatestOvSearchList },
19
+ {
20
+ method: "GET",
21
+ path: RECALL_TRACE_ROUTE_PATHS[3],
22
+ handler: (request) => handle({
23
+ ...request,
24
+ query: {
25
+ ...(request?.query ?? {}),
26
+ traceId: typeof request?.params?.traceId === "string" ? request.params.traceId : undefined,
27
+ },
28
+ }),
29
+ },
30
+ ];
31
+ for (const route of routes) {
32
+ routeAdapter?.registerRoute?.(route);
33
+ }
34
+ if (canRegisterHttpRoute) {
35
+ const sendJson = (res, status, body) => {
36
+ res.statusCode = status;
37
+ res.setHeader?.("Cache-Control", "no-store");
38
+ res.setHeader?.("Content-Type", "application/json; charset=utf-8");
39
+ res.end?.(JSON.stringify(body));
40
+ };
41
+ const makeHttpHandler = (route, getParams) => async (req, res) => {
42
+ if ((req.method ?? "GET").toUpperCase() !== route.method) {
43
+ sendJson(res, 405, { ok: false, error: { code: "method_not_allowed", message: `${route.method} is required` } });
44
+ return true;
45
+ }
46
+ const url = req.url ?? route.path;
47
+ const result = await route.handler({ url, params: getParams?.(url) });
48
+ const response = result;
49
+ sendJson(res, typeof response.status === "number" ? response.status : 200, response.body ?? response);
50
+ return true;
51
+ };
52
+ for (const route of routes) {
53
+ if (route.path === RECALL_TRACE_ROUTE_PATHS[3]) {
54
+ const prefix = "/api/openviking/recall-traces/";
55
+ routeAdapter?.registerHttpRoute?.({
56
+ path: "/api/openviking/recall-traces",
57
+ auth: "plugin",
58
+ match: "prefix",
59
+ handler: makeHttpHandler(route, (url) => {
60
+ const pathname = new URL(url, "http://openclaw.local").pathname;
61
+ const traceId = pathname.startsWith(prefix) ? decodeURIComponent(pathname.slice(prefix.length)) : "";
62
+ return traceId ? { traceId } : {};
63
+ }),
64
+ });
65
+ }
66
+ else {
67
+ routeAdapter?.registerHttpRoute?.({
68
+ path: route.path,
69
+ auth: "plugin",
70
+ match: "exact",
71
+ handler: makeHttpHandler(route),
72
+ });
73
+ }
74
+ }
75
+ }
76
+ return true;
77
+ }
@@ -0,0 +1,23 @@
1
+ export function createOpenVikingToolRegistrar({ api, enabledToolNames, logger, }) {
2
+ return (toolOrFactory, opts) => {
3
+ if (!enabledToolNames.has(opts.name)) {
4
+ logger?.debug?.(`openviking: tool ${opts.name} disabled by config`);
5
+ return;
6
+ }
7
+ api.registerTool(toolOrFactory, opts);
8
+ };
9
+ }
10
+ export function createOpenVikingToolRegistrationRuntime(options) {
11
+ const enabledTools = Array.isArray(options.cfg.enabledTools)
12
+ ? options.cfg.enabledTools
13
+ : [options.cfg.enabledTools];
14
+ const enabledToolNames = new Set(enabledTools);
15
+ const registerOpenVikingTool = createOpenVikingToolRegistrar({
16
+ api: options.api,
17
+ enabledToolNames,
18
+ logger: options.logger,
19
+ });
20
+ return {
21
+ registerOpenVikingTool,
22
+ };
23
+ }
@@ -12,7 +12,7 @@ export function withTimeout(promise, timeoutMs, timeoutMessage) {
12
12
  }
13
13
  export async function quickHealthCheck(client, agentId, timeoutMs) {
14
14
  try {
15
- await client.healthCheck(agentId, timeoutMs);
15
+ await client.healthCheck(timeoutMs, agentId);
16
16
  return true;
17
17
  }
18
18
  catch {