@openviking/openclaw-plugin 2026.5.26 → 2026.6.17-dev.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/INSTALL-AGENT.md +355 -355
  2. package/INSTALL-ZH.md +405 -345
  3. package/INSTALL.md +366 -362
  4. package/README.md +364 -338
  5. package/adapters/http-transport.ts +3 -0
  6. package/adapters/resource-packager.ts +141 -0
  7. package/auto-recall.ts +518 -257
  8. package/client.ts +963 -939
  9. package/commands/setup.ts +1169 -1002
  10. package/config/feature-gates.json +10 -0
  11. package/config.ts +819 -476
  12. package/context-engine.ts +408 -1737
  13. package/dist/adapters/http-transport.js +1 -0
  14. package/dist/adapters/resource-packager.js +114 -0
  15. package/dist/auto-recall.js +228 -32
  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 +64 -1179
  20. package/dist/index.js +209 -1470
  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 +5 -17
  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/dist/token-estimator.js +51 -0
  59. package/index.ts +408 -1985
  60. package/install-manifest.json +57 -47
  61. package/memory-ranking.ts +303 -267
  62. package/openclaw.plugin.json +497 -335
  63. package/package.json +86 -76
  64. package/plugin/command-registration.ts +34 -0
  65. package/plugin/openviking-archive-tools.ts +301 -0
  66. package/plugin/openviking-bypass-runtime.ts +26 -0
  67. package/plugin/openviking-client-runtime.ts +74 -0
  68. package/plugin/openviking-command-args.ts +187 -0
  69. package/plugin/openviking-command-definitions.ts +294 -0
  70. package/plugin/openviking-context-engine-ref.ts +16 -0
  71. package/plugin/openviking-context-engine-registration.ts +97 -0
  72. package/plugin/openviking-feature-gates.ts +335 -0
  73. package/plugin/openviking-import-runtime.ts +105 -0
  74. package/plugin/openviking-import-tools.ts +133 -0
  75. package/plugin/openviking-lifecycle-hooks.ts +65 -0
  76. package/plugin/openviking-memory-recall-tools.ts +368 -0
  77. package/plugin/openviking-memory-tools.ts +299 -0
  78. package/plugin/openviking-query-config-command.ts +230 -0
  79. package/plugin/openviking-query-formatters.ts +108 -0
  80. package/plugin/openviking-query-runtime.ts +476 -0
  81. package/plugin/openviking-query-tools.ts +161 -0
  82. package/plugin/openviking-recall-trace-runtime.ts +482 -0
  83. package/plugin/openviking-recall-trace-tools.ts +99 -0
  84. package/plugin/openviking-runtime-state.ts +52 -0
  85. package/plugin/openviking-runtime-utils.ts +67 -0
  86. package/plugin/openviking-services.ts +49 -0
  87. package/plugin/openviking-session-routing-runtime.ts +105 -0
  88. package/plugin/openviking-tool-result-tools.ts +397 -0
  89. package/plugin/recall-trace-routes.ts +133 -0
  90. package/plugin/tool-registration.ts +51 -0
  91. package/process-manager.ts +41 -45
  92. package/query-config.ts +400 -0
  93. package/recall-trace.ts +442 -0
  94. package/registries/openviking-tools.ts +84 -0
  95. package/registries/recall-resource-types.ts +74 -0
  96. package/routing/identity-routing.ts +210 -0
  97. package/routing/memory-uri.ts +8 -0
  98. package/runtime-utils.ts +5 -5
  99. package/services/context-lifecycle-service.ts +1209 -0
  100. package/services/context-message-adapter.ts +375 -0
  101. package/services/setup/config-writer.ts +125 -0
  102. package/services/setup/exit-utils.ts +5 -0
  103. package/services/setup/package-metadata.ts +56 -0
  104. package/services/setup/probe-service.ts +143 -0
  105. package/services/setup/setup-flow.ts +306 -0
  106. package/services/setup/version-compatibility.ts +41 -0
  107. package/session-transcript-repair.ts +528 -530
  108. package/skills/install-openviking-memory/SKILL.md +752 -665
  109. package/skills/openviking-context-database/SKILL.md +286 -0
  110. package/text-utils.ts +488 -507
  111. package/token-estimator.ts +65 -0
  112. package/tool-call-id.ts +331 -331
@@ -0,0 +1,11 @@
1
+ export function createOpenVikingContextEngineRef() {
2
+ let contextEngineRef = null;
3
+ const getContextEngine = () => contextEngineRef;
4
+ const setContextEngineRef = (engine) => {
5
+ contextEngineRef = engine;
6
+ };
7
+ return {
8
+ getContextEngine,
9
+ setContextEngineRef,
10
+ };
11
+ }
@@ -0,0 +1,23 @@
1
+ export function registerOpenVikingContextEngine(deps) {
2
+ if (typeof deps.api.registerContextEngine !== "function") {
3
+ deps.logger.warn("openviking: registerContextEngine is unavailable; context-engine behavior will not run");
4
+ return;
5
+ }
6
+ deps.api.registerContextEngine(deps.plugin.id, () => {
7
+ const contextEngine = deps.createContextEngine({
8
+ id: deps.plugin.id,
9
+ name: deps.plugin.name,
10
+ version: deps.version,
11
+ cfg: deps.cfg,
12
+ logger: deps.logger,
13
+ getClient: deps.getClient,
14
+ resolveAgentId: deps.resolveAgentId,
15
+ rememberSessionAgentId: deps.rememberSessionAgentId,
16
+ queryConfigStore: deps.queryConfigStore,
17
+ traceRecorder: deps.traceRecorder,
18
+ });
19
+ deps.setContextEngineRef(contextEngine);
20
+ return contextEngine;
21
+ });
22
+ deps.logger.info("openviking: registered context-engine (assemble=archive+active+auto-recall, afterTurn=auto-capture, session→OV id=uuid-or-sha256 + diag/Phase2 options)");
23
+ }
@@ -0,0 +1,250 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { readFile } from "node:fs/promises";
3
+ import { dirname, join } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { Type } from "@sinclair/typebox";
6
+ import { Value } from "@sinclair/typebox/value";
7
+ export const OPENVIKING_260610_FEATURE = "openviking.260610";
8
+ export const OPENVIKING_FEATURE_GATES_RPC = "openviking.feature.gates";
9
+ const versionPattern = String.raw `^\d+\.\d+\.\d+(?:-(?:\d+|beta(?:\.\d+)?))?$`;
10
+ const FeatureGateSchema = Type.Object({
11
+ enable: Type.Boolean(),
12
+ minPluginVersion: Type.String({ pattern: versionPattern }),
13
+ minOpenclawVersion: Type.String({ minLength: 1 }),
14
+ editions: Type.Array(Type.String({ minLength: 1 }), {
15
+ minItems: 1,
16
+ uniqueItems: true,
17
+ }),
18
+ }, { additionalProperties: false });
19
+ const FeatureGatesConfigSchema = Type.Object({
20
+ features: Type.Record(Type.String({ minLength: 1 }), FeatureGateSchema),
21
+ }, { additionalProperties: false });
22
+ function parseVersion(version) {
23
+ const stableMatch = version.match(/^(\d+)\.(\d+)\.(\d+)(?:-(\d+))?$/);
24
+ if (stableMatch) {
25
+ return {
26
+ year: Number(stableMatch[1]),
27
+ month: Number(stableMatch[2]),
28
+ day: Number(stableMatch[3]),
29
+ channelRank: 1,
30
+ sequence: stableMatch[4] ? Number(stableMatch[4]) : 0,
31
+ };
32
+ }
33
+ const betaMatch = version.match(/^(\d+)\.(\d+)\.(\d+)-beta(?:\.(\d+))?$/);
34
+ if (betaMatch) {
35
+ return {
36
+ year: Number(betaMatch[1]),
37
+ month: Number(betaMatch[2]),
38
+ day: Number(betaMatch[3]),
39
+ channelRank: 0,
40
+ sequence: betaMatch[4] ? Number(betaMatch[4]) : 0,
41
+ };
42
+ }
43
+ return null;
44
+ }
45
+ export function isPluginVersionAtLeast(currentVersion, minVersion) {
46
+ const current = parseVersion(currentVersion);
47
+ const minimum = parseVersion(minVersion);
48
+ if (!current || !minimum) {
49
+ return false;
50
+ }
51
+ if (current.year !== minimum.year)
52
+ return current.year > minimum.year;
53
+ if (current.month !== minimum.month)
54
+ return current.month > minimum.month;
55
+ if (current.day !== minimum.day)
56
+ return current.day > minimum.day;
57
+ if (current.channelRank !== minimum.channelRank) {
58
+ return current.channelRank > minimum.channelRank;
59
+ }
60
+ return current.sequence >= minimum.sequence;
61
+ }
62
+ export function isOpenClawVersionAtLeast(currentVersion, minVersion) {
63
+ return isPluginVersionAtLeast(currentVersion, minVersion);
64
+ }
65
+ function formatSchemaError(path, message) {
66
+ const normalizedPath = path ? path.replace(/\//g, ".").replace(/^\./, "") : "$";
67
+ return `${normalizedPath}: ${message}`;
68
+ }
69
+ function parseFeatureGatesConfig(raw) {
70
+ const parsed = JSON.parse(raw);
71
+ if (Value.Check(FeatureGatesConfigSchema, parsed)) {
72
+ return parsed;
73
+ }
74
+ const validationError = Value.Errors(FeatureGatesConfigSchema, parsed).First();
75
+ const errorMessage = validationError
76
+ ? formatSchemaError(validationError.path, validationError.message)
77
+ : "unknown validation error";
78
+ throw new Error(`Invalid feature-gates.json: ${errorMessage}`);
79
+ }
80
+ function normalizeFeatures(features, pluginVersion, openclawVersion, edition) {
81
+ const requestedEdition = edition?.trim();
82
+ const businessCarriers = (process.env.BUSINESS_CARRIER ?? "")
83
+ .split(",")
84
+ .map((carrier) => carrier.trim())
85
+ .filter(Boolean);
86
+ const effectiveEditions = requestedEdition ? [requestedEdition] : businessCarriers;
87
+ return Object.entries(features).flatMap(([featureName, featureConfig]) => {
88
+ const editionOk = effectiveEditions.length > 0 &&
89
+ effectiveEditions.some((carrier) => featureConfig.editions.includes(carrier));
90
+ if (featureConfig.enable &&
91
+ editionOk &&
92
+ isPluginVersionAtLeast(pluginVersion, featureConfig.minPluginVersion) &&
93
+ isOpenClawVersionAtLeast(openclawVersion, featureConfig.minOpenclawVersion)) {
94
+ return [featureName];
95
+ }
96
+ return [];
97
+ });
98
+ }
99
+ let cachedPackageRoot;
100
+ let cachedDefaultPluginVersion;
101
+ function findPackageRoot(startDir) {
102
+ let current = startDir;
103
+ for (;;) {
104
+ if (existsSync(join(current, "package.json"))) {
105
+ return current;
106
+ }
107
+ const parent = dirname(current);
108
+ if (parent === current) {
109
+ return startDir;
110
+ }
111
+ current = parent;
112
+ }
113
+ }
114
+ function getPackageRoot() {
115
+ cachedPackageRoot ??= findPackageRoot(dirname(fileURLToPath(import.meta.url)));
116
+ return cachedPackageRoot;
117
+ }
118
+ function getDefaultFeatureGatesPath() {
119
+ return join(getPackageRoot(), "config", "feature-gates.json");
120
+ }
121
+ function getDefaultPluginVersion() {
122
+ if (cachedDefaultPluginVersion) {
123
+ return cachedDefaultPluginVersion;
124
+ }
125
+ try {
126
+ const raw = readFileSync(join(getPackageRoot(), "package.json"), "utf8");
127
+ const parsed = JSON.parse(raw);
128
+ cachedDefaultPluginVersion = typeof parsed.version === "string" ? parsed.version : "0.0.0";
129
+ }
130
+ catch {
131
+ cachedDefaultPluginVersion = "0.0.0";
132
+ }
133
+ return cachedDefaultPluginVersion;
134
+ }
135
+ function getDefaultOpenClawVersionSync() {
136
+ const envVersion = process.env.OPENCLAW_VERSION?.trim();
137
+ return envVersion || "2026.4.8";
138
+ }
139
+ export function createOpenVikingFeatureGateService(options = {}) {
140
+ const configPath = options.configPath ?? getDefaultFeatureGatesPath();
141
+ const getPluginVersion = options.getPluginVersion ?? getDefaultPluginVersion;
142
+ const getOpenClawVersion = options.getOpenClawVersion ?? (async () => getDefaultOpenClawVersionSync());
143
+ const getOpenClawVersionSync = options.getOpenClawVersionSync ?? getDefaultOpenClawVersionSync;
144
+ let cachedConfig;
145
+ let cachedConfigPromise;
146
+ let cachedOpenClawVersion;
147
+ let cachedOpenClawVersionPromise;
148
+ async function loadFeatureGatesConfig() {
149
+ if (cachedConfig) {
150
+ return cachedConfig;
151
+ }
152
+ cachedConfigPromise ??= readFile(configPath, "utf8")
153
+ .then((raw) => {
154
+ cachedConfig = parseFeatureGatesConfig(raw);
155
+ return cachedConfig;
156
+ })
157
+ .catch((error) => {
158
+ cachedConfigPromise = undefined;
159
+ throw error;
160
+ });
161
+ return cachedConfigPromise;
162
+ }
163
+ function loadFeatureGatesConfigSync() {
164
+ if (cachedConfig) {
165
+ return cachedConfig;
166
+ }
167
+ const raw = readFileSync(configPath, "utf8");
168
+ cachedConfig = parseFeatureGatesConfig(raw);
169
+ return cachedConfig;
170
+ }
171
+ async function loadOpenClawVersion() {
172
+ if (cachedOpenClawVersion) {
173
+ return cachedOpenClawVersion;
174
+ }
175
+ cachedOpenClawVersionPromise ??= getOpenClawVersion()
176
+ .then((version) => {
177
+ cachedOpenClawVersion = version;
178
+ return version;
179
+ })
180
+ .catch((error) => {
181
+ cachedOpenClawVersionPromise = undefined;
182
+ throw error;
183
+ });
184
+ return cachedOpenClawVersionPromise;
185
+ }
186
+ function loadOpenClawVersionSync() {
187
+ cachedOpenClawVersion ??= getOpenClawVersionSync();
188
+ return cachedOpenClawVersion;
189
+ }
190
+ function loadAndNormalizeFeaturesSync(edition) {
191
+ const parsed = loadFeatureGatesConfigSync();
192
+ return normalizeFeatures(parsed.features, getPluginVersion(), loadOpenClawVersionSync(), edition);
193
+ }
194
+ async function loadAndNormalizeFeatures(edition) {
195
+ const [parsed, openclawVersion] = await Promise.all([
196
+ loadFeatureGatesConfig(),
197
+ loadOpenClawVersion(),
198
+ ]);
199
+ return normalizeFeatures(parsed.features, getPluginVersion(), openclawVersion, edition);
200
+ }
201
+ return {
202
+ async getEnabledFeatureGates(edition) {
203
+ return loadAndNormalizeFeatures(edition);
204
+ },
205
+ async isFeatureGateEnabled(featureName, edition) {
206
+ try {
207
+ const features = await loadAndNormalizeFeatures(edition);
208
+ return features.includes(featureName);
209
+ }
210
+ catch {
211
+ return false;
212
+ }
213
+ },
214
+ isFeatureGateEnabledSync(featureName, edition) {
215
+ try {
216
+ const features = loadAndNormalizeFeaturesSync(edition);
217
+ return features.includes(featureName);
218
+ }
219
+ catch {
220
+ return false;
221
+ }
222
+ },
223
+ };
224
+ }
225
+ function readEditionParam(params) {
226
+ if (!params || typeof params !== "object") {
227
+ return undefined;
228
+ }
229
+ const value = params.edition;
230
+ return typeof value === "string" ? value : undefined;
231
+ }
232
+ const defaultFeatureGateService = createOpenVikingFeatureGateService();
233
+ export const getEnabledFeatureGates = (edition) => defaultFeatureGateService.getEnabledFeatureGates(edition);
234
+ export const isFeatureGateEnabled = (featureName, edition) => defaultFeatureGateService.isFeatureGateEnabled(featureName, edition);
235
+ export const isFeatureGateEnabledSync = (featureName, edition) => defaultFeatureGateService.isFeatureGateEnabledSync(featureName, edition);
236
+ export function registerOpenVikingFeatureGatesMethod(api, service = defaultFeatureGateService) {
237
+ if (typeof api.registerGatewayMethod !== "function") {
238
+ return;
239
+ }
240
+ api.registerGatewayMethod(OPENVIKING_FEATURE_GATES_RPC, async ({ params, respond }) => {
241
+ try {
242
+ const edition = readEditionParam(params);
243
+ const features = await service.getEnabledFeatureGates(edition);
244
+ respond(true, { features });
245
+ }
246
+ catch (error) {
247
+ respond(false, error instanceof Error ? error.message : String(error));
248
+ }
249
+ });
250
+ }
@@ -0,0 +1,50 @@
1
+ function formatResourceImportText(result) {
2
+ const root = result.root_uri ? ` ${result.root_uri}` : "";
3
+ const warnings = result.warnings?.length ? ` Warnings: ${result.warnings.join("; ")}` : "";
4
+ return `Imported OpenViking resource.${root}${warnings}`.trim();
5
+ }
6
+ function formatSkillImportText(result) {
7
+ const uri = result.uri ? ` ${result.uri}` : "";
8
+ const name = result.name ? ` (${result.name})` : "";
9
+ return `Imported OpenViking skill${name}.${uri}`.trim();
10
+ }
11
+ export function createOpenVikingImportRuntime(deps) {
12
+ const importResource = async (input, agentId) => {
13
+ const client = await deps.getClient();
14
+ const result = await client.addResource(input, agentId);
15
+ return {
16
+ content: [{ type: "text", text: formatResourceImportText(result) }],
17
+ details: {
18
+ action: "resource_imported",
19
+ ...result,
20
+ },
21
+ };
22
+ };
23
+ const importSkill = async (input, agentId) => {
24
+ const client = await deps.getClient();
25
+ const result = await client.addSkill(input, agentId);
26
+ return {
27
+ content: [{ type: "text", text: formatSkillImportText(result) }],
28
+ details: {
29
+ action: "skill_imported",
30
+ ...result,
31
+ },
32
+ };
33
+ };
34
+ const addResourceOpenViking = (input, agentId) => importResource({
35
+ pathOrUrl: input.source ?? "",
36
+ to: input.to,
37
+ parent: input.parent,
38
+ reason: input.reason,
39
+ instruction: input.instruction,
40
+ wait: input.wait,
41
+ timeout: input.timeout,
42
+ }, agentId);
43
+ const addSkillOpenViking = (input, agentId) => importSkill({
44
+ path: input.source,
45
+ data: input.data,
46
+ wait: input.wait,
47
+ timeout: input.timeout,
48
+ }, agentId);
49
+ return { addResourceOpenViking, addSkillOpenViking };
50
+ }
@@ -0,0 +1,86 @@
1
+ import { Type } from "@sinclair/typebox";
2
+ function formatResourceImportText(result) {
3
+ const root = result.root_uri ? ` ${result.root_uri}` : "";
4
+ const warnings = result.warnings?.length ? ` Warnings: ${result.warnings.join("; ")}` : "";
5
+ return `Imported OpenViking resource.${root}${warnings}`.trim();
6
+ }
7
+ function formatSkillImportText(result) {
8
+ const uri = result.uri ? ` ${result.uri}` : "";
9
+ const name = result.name ? ` (${result.name})` : "";
10
+ return `Imported OpenViking skill${name}.${uri}`.trim();
11
+ }
12
+ export function registerOpenVikingImportTools(deps) {
13
+ if (deps.enableAddResourceTool) {
14
+ deps.registerTool((ctx) => ({
15
+ name: "add_resource",
16
+ label: "Add Resource (OpenViking)",
17
+ description: "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. " +
18
+ "Never use this during search, retrieval, URI reading, or search-result optimization; use ov_search and ov_read for those flows. " +
19
+ "For a '[media attached: /path ...]' document, set source to that exact local media path. Do not invent OpenViking upload REST endpoints.",
20
+ parameters: Type.Object({
21
+ source: Type.String({ description: "Local path, OpenClaw media attachment path, directory path, public URL, or Git URL" }),
22
+ to: Type.Optional(Type.String({ description: "Exact target URI, e.g. viking://resources/project-docs" })),
23
+ parent: Type.Optional(Type.String({ description: "Parent URI under viking://resources" })),
24
+ reason: Type.Optional(Type.String({ description: "Reason or note for adding this resource" })),
25
+ instruction: Type.Optional(Type.String({ description: "Processing instruction for semantic extraction" })),
26
+ wait: Type.Optional(Type.Boolean({ description: "Wait for processing to complete" })),
27
+ timeout: Type.Optional(Type.Number({ description: "Timeout in seconds when wait is true" })),
28
+ }),
29
+ async execute(_toolCallId, params) {
30
+ if (deps.isBypassedSession(ctx)) {
31
+ return deps.makeBypassedToolResult("add_resource");
32
+ }
33
+ const session = deps.resolvePluginSessionRouting(ctx);
34
+ const client = await deps.getClient();
35
+ const result = await client.addResource({
36
+ pathOrUrl: typeof params.source === "string" ? params.source : "",
37
+ to: typeof params.to === "string" ? params.to : undefined,
38
+ parent: typeof params.parent === "string" ? params.parent : undefined,
39
+ reason: typeof params.reason === "string" ? params.reason : undefined,
40
+ instruction: typeof params.instruction === "string" ? params.instruction : undefined,
41
+ wait: typeof params.wait === "boolean" ? params.wait : undefined,
42
+ timeout: typeof params.timeout === "number" ? params.timeout : undefined,
43
+ }, session.agentId);
44
+ return {
45
+ content: [{ type: "text", text: formatResourceImportText(result) }],
46
+ details: {
47
+ action: "resource_imported",
48
+ ...result,
49
+ },
50
+ };
51
+ },
52
+ }), { name: "add_resource" });
53
+ }
54
+ deps.registerTool((ctx) => ({
55
+ name: "add_skill",
56
+ label: "Add Skill (OpenViking)",
57
+ description: "Use only when the user explicitly asks to import, add, install, or register a skill into OpenViking. " +
58
+ "Set source to a local SKILL.md file or skill directory, or data to raw SKILL.md content or an MCP tool dict.",
59
+ parameters: Type.Object({
60
+ source: Type.Optional(Type.String({ description: "Local SKILL.md path or skill directory path" })),
61
+ data: Type.Optional(Type.Any({ description: "Raw SKILL.md content or MCP tool dict" })),
62
+ wait: Type.Optional(Type.Boolean({ description: "Wait for processing to complete" })),
63
+ timeout: Type.Optional(Type.Number({ description: "Timeout in seconds when wait is true" })),
64
+ }),
65
+ async execute(_toolCallId, params) {
66
+ if (deps.isBypassedSession(ctx)) {
67
+ return deps.makeBypassedToolResult("add_skill");
68
+ }
69
+ const session = deps.resolvePluginSessionRouting(ctx);
70
+ const client = await deps.getClient();
71
+ const result = await client.addSkill({
72
+ path: typeof params.source === "string" ? params.source : undefined,
73
+ data: params.data,
74
+ wait: typeof params.wait === "boolean" ? params.wait : undefined,
75
+ timeout: typeof params.timeout === "number" ? params.timeout : undefined,
76
+ }, session.agentId);
77
+ return {
78
+ content: [{ type: "text", text: formatSkillImportText(result) }],
79
+ details: {
80
+ action: "skill_imported",
81
+ ...result,
82
+ },
83
+ };
84
+ },
85
+ }), { name: "add_skill" });
86
+ }
@@ -0,0 +1,33 @@
1
+ export function registerOpenVikingLifecycleHooks(deps) {
2
+ deps.api.on("session_start", async (_event, ctx) => {
3
+ deps.rememberSessionAgentId(ctx ?? {});
4
+ });
5
+ deps.api.on("session_end", async (_event, ctx) => {
6
+ deps.rememberSessionAgentId(ctx ?? {});
7
+ });
8
+ deps.api.on("before_reset", async (_event, ctx) => {
9
+ if (deps.isBypassedSession(ctx)) {
10
+ deps.verboseRoutingInfo(`openviking: bypassing before_reset due to session pattern match (sessionKey=${ctx?.sessionKey ?? "none"}, sessionId=${ctx?.sessionId ?? "none"})`);
11
+ return;
12
+ }
13
+ const sessionId = ctx?.sessionId;
14
+ const contextEngine = deps.getContextEngine();
15
+ if (sessionId && contextEngine) {
16
+ try {
17
+ const ok = await contextEngine.commitOVSession({
18
+ sessionId,
19
+ sessionKey: ctx?.sessionKey,
20
+ });
21
+ if (ok) {
22
+ deps.logger.info(`openviking: committed OV session on reset for session=${sessionId}`);
23
+ }
24
+ }
25
+ catch (err) {
26
+ deps.logger.warn(`openviking: failed to commit OV session on reset: ${String(err)}`);
27
+ }
28
+ }
29
+ });
30
+ deps.api.on("after_compaction", async (_event, _ctx) => {
31
+ // Reserved hook registration for future post-compaction memory integration.
32
+ });
33
+ }