@openviking/openclaw-plugin 2026.6.18 → 2026.7.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/INSTALL-AGENT.md +2 -2
- package/INSTALL-ZH.md +2 -2
- package/INSTALL.md +2 -2
- package/README.md +1 -1
- package/auto-recall.ts +1 -2
- package/client.ts +14 -6
- package/commands/setup.ts +40 -10
- package/config.ts +44 -8
- package/context-engine.ts +2 -0
- package/dist/auto-recall.js +1 -2
- package/dist/client.js +11 -4
- package/dist/commands/setup.js +26 -10
- package/dist/config.js +31 -6
- package/dist/context-engine.js +2 -0
- package/dist/plugin/openviking-client-runtime.js +7 -1
- package/dist/plugin/openviking-memory-tools.js +1 -1
- package/dist/query-config.js +7 -1
- package/dist/request-headers.js +19 -0
- package/dist/services/context-lifecycle-service.js +35 -7
- package/dist/text-utils.js +1 -6
- package/install-manifest.json +2 -1
- package/openclaw.plugin.json +20 -4
- package/package.json +6 -6
- package/plugin/openviking-client-runtime.ts +8 -1
- package/plugin/openviking-memory-tools.ts +1 -1
- package/query-config.ts +7 -1
- package/request-headers.ts +29 -0
- package/services/context-lifecycle-service.ts +42 -7
- package/services/setup/probe-service.ts +35 -6
- package/services/setup/setup-flow.ts +61 -7
- package/skills/install-openviking-memory/SKILL.md +29 -15
- package/skills/openviking-context-database/SKILL.md +3 -3
- package/text-utils.ts +1 -6
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import {
|
|
2
|
+
resolveOpenVikingRequestHeaders,
|
|
3
|
+
type OpenVikingRequestHeaders,
|
|
4
|
+
} from "../../request-headers.js";
|
|
5
|
+
|
|
1
6
|
export type SetupNetworkTransport = (url: string, init: RequestInit) => Promise<Response>;
|
|
2
7
|
|
|
3
8
|
export type SetupVersionCompatibility = "compatible" | "server_too_old" | "server_too_new" | "unknown";
|
|
@@ -27,8 +32,16 @@ export type SetupNetworkProbeOptions = {
|
|
|
27
32
|
};
|
|
28
33
|
|
|
29
34
|
export type SetupNetworkProbes = {
|
|
30
|
-
probeApiKeyType: (
|
|
31
|
-
|
|
35
|
+
probeApiKeyType: (
|
|
36
|
+
baseUrl: string,
|
|
37
|
+
apiKey?: string,
|
|
38
|
+
headers?: OpenVikingRequestHeaders,
|
|
39
|
+
) => Promise<SetupApiKeyProbeResult>;
|
|
40
|
+
checkServiceHealth: (
|
|
41
|
+
baseUrl: string,
|
|
42
|
+
apiKey?: string,
|
|
43
|
+
headers?: OpenVikingRequestHeaders,
|
|
44
|
+
) => Promise<SetupHealthResult>;
|
|
32
45
|
};
|
|
33
46
|
|
|
34
47
|
export const defaultSetupNetworkTransport: SetupNetworkTransport = (url, init) => fetch(url, init);
|
|
@@ -41,7 +54,11 @@ export function createSetupNetworkProbes({
|
|
|
41
54
|
timeoutMs = 10_000,
|
|
42
55
|
}: SetupNetworkProbeOptions): SetupNetworkProbes {
|
|
43
56
|
return {
|
|
44
|
-
async probeApiKeyType(
|
|
57
|
+
async probeApiKeyType(
|
|
58
|
+
baseUrl: string,
|
|
59
|
+
apiKey?: string,
|
|
60
|
+
configuredHeaders?: OpenVikingRequestHeaders,
|
|
61
|
+
): Promise<SetupApiKeyProbeResult> {
|
|
45
62
|
if (!apiKey) {
|
|
46
63
|
return { keyType: "no_key", needsAccountId: false, needsUserId: false, detail: "No API key configured" };
|
|
47
64
|
}
|
|
@@ -50,7 +67,10 @@ export function createSetupNetworkProbes({
|
|
|
50
67
|
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
51
68
|
const sessionsUrl = `${baseUrl.replace(/\/+$/, "")}/api/v1/sessions?limit=1`;
|
|
52
69
|
try {
|
|
53
|
-
const
|
|
70
|
+
const configuredRequestHeaders = resolveOpenVikingRequestHeaders({ headers: configuredHeaders });
|
|
71
|
+
const headers: Record<string, string> = {};
|
|
72
|
+
headers["X-API-Key"] = apiKey;
|
|
73
|
+
Object.assign(headers, configuredRequestHeaders);
|
|
54
74
|
const response = await transport(sessionsUrl, {
|
|
55
75
|
headers,
|
|
56
76
|
signal: controller.signal,
|
|
@@ -82,6 +102,7 @@ export function createSetupNetworkProbes({
|
|
|
82
102
|
"X-API-Key": apiKey,
|
|
83
103
|
"X-OpenViking-Account": "__probe__",
|
|
84
104
|
"X-OpenViking-User": "__probe__",
|
|
105
|
+
...configuredRequestHeaders,
|
|
85
106
|
};
|
|
86
107
|
const probe2 = await transport(sessionsUrl, {
|
|
87
108
|
headers: probeHeaders,
|
|
@@ -111,12 +132,20 @@ export function createSetupNetworkProbes({
|
|
|
111
132
|
}
|
|
112
133
|
},
|
|
113
134
|
|
|
114
|
-
async checkServiceHealth(
|
|
135
|
+
async checkServiceHealth(
|
|
136
|
+
baseUrl: string,
|
|
137
|
+
apiKey?: string,
|
|
138
|
+
configuredHeaders?: OpenVikingRequestHeaders,
|
|
139
|
+
): Promise<SetupHealthResult> {
|
|
115
140
|
const controller = new AbortController();
|
|
116
141
|
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
117
142
|
try {
|
|
143
|
+
const configuredRequestHeaders = resolveOpenVikingRequestHeaders({ headers: configuredHeaders });
|
|
118
144
|
const headers: Record<string, string> = {};
|
|
119
|
-
if (apiKey)
|
|
145
|
+
if (apiKey) {
|
|
146
|
+
headers["X-API-Key"] = apiKey;
|
|
147
|
+
}
|
|
148
|
+
Object.assign(headers, configuredRequestHeaders);
|
|
120
149
|
const response = await transport(`${baseUrl.replace(/\/+$/, "")}/health`, {
|
|
121
150
|
headers,
|
|
122
151
|
signal: controller.signal,
|
|
@@ -6,6 +6,10 @@ import {
|
|
|
6
6
|
type SetupIO,
|
|
7
7
|
type SlotActivationResult,
|
|
8
8
|
} from "./config-writer.js";
|
|
9
|
+
import {
|
|
10
|
+
cleanOpenVikingRequestHeaders,
|
|
11
|
+
type OpenVikingRequestHeaders,
|
|
12
|
+
} from "../../request-headers.js";
|
|
9
13
|
|
|
10
14
|
export type { SetupIO, SlotActivationResult };
|
|
11
15
|
|
|
@@ -38,6 +42,7 @@ export type SetupResult = {
|
|
|
38
42
|
peer_prefix?: string;
|
|
39
43
|
accountId?: string;
|
|
40
44
|
userId?: string;
|
|
45
|
+
headers?: OpenVikingRequestHeaders;
|
|
41
46
|
recallTargetTypes?: string[];
|
|
42
47
|
};
|
|
43
48
|
health?: HealthResult;
|
|
@@ -65,6 +70,7 @@ export type StatusResult = {
|
|
|
65
70
|
export type SetupParams = {
|
|
66
71
|
baseUrl: string;
|
|
67
72
|
apiKey?: string;
|
|
73
|
+
headers?: OpenVikingRequestHeaders;
|
|
68
74
|
peerRole?: "none" | "assistant" | "person";
|
|
69
75
|
peerPrefix?: string;
|
|
70
76
|
accountId?: string;
|
|
@@ -78,6 +84,7 @@ export type InteractiveRemoteConfigParams = {
|
|
|
78
84
|
existing?: Record<string, unknown> | null;
|
|
79
85
|
baseUrl: string;
|
|
80
86
|
apiKey?: string;
|
|
87
|
+
headers?: OpenVikingRequestHeaders;
|
|
81
88
|
peerRole?: "none" | "assistant" | "person";
|
|
82
89
|
peerPrefix?: string;
|
|
83
90
|
accountId?: string;
|
|
@@ -100,8 +107,16 @@ export type OpenVikingSetupService = {
|
|
|
100
107
|
export type OpenVikingSetupServiceDependencies = {
|
|
101
108
|
io: SetupIO;
|
|
102
109
|
defaultRemoteUrl?: string;
|
|
103
|
-
checkServiceHealth: (
|
|
104
|
-
|
|
110
|
+
checkServiceHealth: (
|
|
111
|
+
baseUrl: string,
|
|
112
|
+
apiKey?: string,
|
|
113
|
+
headers?: OpenVikingRequestHeaders,
|
|
114
|
+
) => Promise<HealthResult>;
|
|
115
|
+
probeApiKeyType: (
|
|
116
|
+
baseUrl: string,
|
|
117
|
+
apiKey?: string,
|
|
118
|
+
headers?: OpenVikingRequestHeaders,
|
|
119
|
+
) => Promise<ApiKeyProbeResult>;
|
|
105
120
|
};
|
|
106
121
|
|
|
107
122
|
export function maskKey(key: string): string {
|
|
@@ -114,10 +129,38 @@ export function isLegacyLocalMode(existing: Record<string, unknown>): boolean {
|
|
|
114
129
|
return mode !== "remote";
|
|
115
130
|
}
|
|
116
131
|
|
|
132
|
+
function nonEmptyOpenVikingRequestHeaders(value: unknown): OpenVikingRequestHeaders | undefined {
|
|
133
|
+
const headers = cleanOpenVikingRequestHeaders(value);
|
|
134
|
+
return Object.keys(headers).length > 0 ? headers : undefined;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function callHealthProbe(
|
|
138
|
+
checkServiceHealth: OpenVikingSetupServiceDependencies["checkServiceHealth"],
|
|
139
|
+
baseUrl: string,
|
|
140
|
+
apiKey: string | undefined,
|
|
141
|
+
headers: OpenVikingRequestHeaders | undefined,
|
|
142
|
+
): Promise<HealthResult> {
|
|
143
|
+
return headers
|
|
144
|
+
? checkServiceHealth(baseUrl, apiKey, headers)
|
|
145
|
+
: checkServiceHealth(baseUrl, apiKey);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function callApiKeyProbe(
|
|
149
|
+
probeApiKeyType: OpenVikingSetupServiceDependencies["probeApiKeyType"],
|
|
150
|
+
baseUrl: string,
|
|
151
|
+
apiKey: string | undefined,
|
|
152
|
+
headers: OpenVikingRequestHeaders | undefined,
|
|
153
|
+
): Promise<ApiKeyProbeResult> {
|
|
154
|
+
return headers
|
|
155
|
+
? probeApiKeyType(baseUrl, apiKey, headers)
|
|
156
|
+
: probeApiKeyType(baseUrl, apiKey);
|
|
157
|
+
}
|
|
158
|
+
|
|
117
159
|
export function buildInteractiveRemotePluginConfig({
|
|
118
160
|
existing,
|
|
119
161
|
baseUrl,
|
|
120
162
|
apiKey,
|
|
163
|
+
headers,
|
|
121
164
|
peerRole,
|
|
122
165
|
peerPrefix,
|
|
123
166
|
accountId,
|
|
@@ -138,6 +181,11 @@ export function buildInteractiveRemotePluginConfig({
|
|
|
138
181
|
else delete pluginCfg.accountId;
|
|
139
182
|
if (userId) pluginCfg.userId = userId;
|
|
140
183
|
else delete pluginCfg.userId;
|
|
184
|
+
if (headers !== undefined) {
|
|
185
|
+
const cleanHeaders = nonEmptyOpenVikingRequestHeaders(headers);
|
|
186
|
+
if (cleanHeaders) pluginCfg.headers = cleanHeaders;
|
|
187
|
+
else delete pluginCfg.headers;
|
|
188
|
+
}
|
|
141
189
|
delete pluginCfg.configPath;
|
|
142
190
|
delete pluginCfg.port;
|
|
143
191
|
return pluginCfg;
|
|
@@ -153,8 +201,9 @@ export function createOpenVikingSetupService({
|
|
|
153
201
|
async setupNonInteractive(configPath: string, params: SetupParams): Promise<SetupResult> {
|
|
154
202
|
try {
|
|
155
203
|
const { baseUrl, apiKey, peerRole, peerPrefix, accountId, userId, recallTargetTypes, allowOffline, forceSlot } = params;
|
|
204
|
+
const headers = nonEmptyOpenVikingRequestHeaders(params.headers);
|
|
156
205
|
|
|
157
|
-
const health = await checkServiceHealth
|
|
206
|
+
const health = await callHealthProbe(checkServiceHealth, baseUrl, apiKey, headers);
|
|
158
207
|
|
|
159
208
|
if (!health.ok && !allowOffline) {
|
|
160
209
|
return {
|
|
@@ -167,7 +216,7 @@ export function createOpenVikingSetupService({
|
|
|
167
216
|
};
|
|
168
217
|
}
|
|
169
218
|
|
|
170
|
-
const keyProbe = health.ok ? await probeApiKeyType
|
|
219
|
+
const keyProbe = health.ok ? await callApiKeyProbe(probeApiKeyType, baseUrl, apiKey, headers) : undefined;
|
|
171
220
|
|
|
172
221
|
if (keyProbe?.keyType === "root_key" && (!accountId || !userId)) {
|
|
173
222
|
const missing: string[] = [];
|
|
@@ -190,6 +239,7 @@ export function createOpenVikingSetupService({
|
|
|
190
239
|
|
|
191
240
|
const pluginCfg: Record<string, unknown> = { mode: "remote", baseUrl };
|
|
192
241
|
if (apiKey) pluginCfg.apiKey = apiKey;
|
|
242
|
+
if (headers && Object.keys(headers).length > 0) pluginCfg.headers = headers;
|
|
193
243
|
if (peerRole) pluginCfg.peer_role = peerRole;
|
|
194
244
|
if (peerPrefix) pluginCfg.peer_prefix = peerPrefix;
|
|
195
245
|
if (accountId) pluginCfg.accountId = accountId;
|
|
@@ -203,6 +253,7 @@ export function createOpenVikingSetupService({
|
|
|
203
253
|
mode: "remote",
|
|
204
254
|
baseUrl,
|
|
205
255
|
...(apiKey ? { apiKey: maskKey(apiKey) } : {}),
|
|
256
|
+
...(headers && Object.keys(headers).length > 0 ? { headers } : {}),
|
|
206
257
|
...(peerRole ? { peer_role: peerRole } : {}),
|
|
207
258
|
...(peerPrefix ? { peer_prefix: peerPrefix } : {}),
|
|
208
259
|
...(accountId ? { accountId } : {}),
|
|
@@ -253,7 +304,8 @@ export function createOpenVikingSetupService({
|
|
|
253
304
|
async useExistingRemoteConfig(configPath: string, existing: Record<string, unknown>): Promise<SetupResult> {
|
|
254
305
|
const baseUrl = String(existing.baseUrl ?? defaultRemoteUrl);
|
|
255
306
|
const apiKey = existing.apiKey ? String(existing.apiKey) : undefined;
|
|
256
|
-
const
|
|
307
|
+
const headers = nonEmptyOpenVikingRequestHeaders(existing.headers);
|
|
308
|
+
const health = await callHealthProbe(checkServiceHealth, baseUrl, apiKey, headers);
|
|
257
309
|
const slot = activateContextEngineSlot(configPath, false, io);
|
|
258
310
|
return {
|
|
259
311
|
success: true,
|
|
@@ -262,6 +314,7 @@ export function createOpenVikingSetupService({
|
|
|
262
314
|
mode: String(existing.mode ?? "remote"),
|
|
263
315
|
baseUrl,
|
|
264
316
|
...(apiKey ? { apiKey: maskKey(apiKey) } : {}),
|
|
317
|
+
...(headers ? { headers } : {}),
|
|
265
318
|
...(existing.peer_role ? { peer_role: String(existing.peer_role) as "none" | "assistant" | "person" } : {}),
|
|
266
319
|
...(existing.peer_prefix ? { peer_prefix: String(existing.peer_prefix) } : {}),
|
|
267
320
|
...(existing.accountId ? { accountId: String(existing.accountId) } : {}),
|
|
@@ -283,8 +336,9 @@ export function createOpenVikingSetupService({
|
|
|
283
336
|
|
|
284
337
|
const baseUrl = String(existing.baseUrl ?? defaultRemoteUrl);
|
|
285
338
|
const apiKey = existing.apiKey ? String(existing.apiKey) : undefined;
|
|
286
|
-
const
|
|
287
|
-
const
|
|
339
|
+
const headers = nonEmptyOpenVikingRequestHeaders(existing.headers);
|
|
340
|
+
const health = await callHealthProbe(checkServiceHealth, baseUrl, apiKey, headers);
|
|
341
|
+
const keyProbe = health.ok ? await callApiKeyProbe(probeApiKeyType, baseUrl, apiKey, headers) : undefined;
|
|
288
342
|
|
|
289
343
|
return {
|
|
290
344
|
configured: true,
|
|
@@ -91,8 +91,8 @@ openclaw --version
|
|
|
91
91
|
|
|
92
92
|
**If `openclaw` is missing:**
|
|
93
93
|
|
|
94
|
-
> (CN) 你的系统没有安装 OpenClaw。请先安装 OpenClaw(>= 2026.
|
|
95
|
-
> (EN) OpenClaw is not installed. Please install OpenClaw (>= 2026.
|
|
94
|
+
> (CN) 你的系统没有安装 OpenClaw。请先安装 OpenClaw(>= 2026.5.27),然后再回来找我。
|
|
95
|
+
> (EN) OpenClaw is not installed. Please install OpenClaw (>= 2026.5.27) first, then come back.
|
|
96
96
|
|
|
97
97
|
**Stop. Do NOT continue.**
|
|
98
98
|
|
|
@@ -413,20 +413,22 @@ Expected output:
|
|
|
413
413
|
> (CN) 🎉 全部搞定!OpenViking 长期记忆已经接好了。
|
|
414
414
|
>
|
|
415
415
|
> 工作方式:
|
|
416
|
-
> -
|
|
417
|
-
> -
|
|
416
|
+
> - **每一轮**:你的对话会被自动追加到 OpenViking session 里;自动抽取依赖阈值 commit 或 `/compact`
|
|
417
|
+
> - **明确要记住时**:如果你说“记住/保存/存一下”某个长期事实,我会用 `memory_store` 立即提交到记忆管线
|
|
418
|
+
> - **`/compact` 时**:待提交的 session 消息会在 OpenViking 服务端被抽取成长期记忆
|
|
418
419
|
> - **后续会话**:每次回复前我都会自动搜一下相关记忆并带进上下文
|
|
419
420
|
>
|
|
420
|
-
>
|
|
421
|
+
> 想验证一下吗?可以直接说“记住我的邮箱是 test@example.com”,让我通过 `memory_store` 立即提交;或者先聊几句包含可记忆事实的话,再调一下 `/compact`。之后开新对话问“我的邮箱是?”,应该就能召回了。
|
|
421
422
|
|
|
422
423
|
> (EN) 🎉 All set! OpenViking long-term memory is connected.
|
|
423
424
|
>
|
|
424
425
|
> How it works:
|
|
425
|
-
> - **Every turn**: our messages are
|
|
426
|
-
> - **
|
|
426
|
+
> - **Every turn**: our messages are appended to an OpenViking session; automatic extraction depends on a threshold commit or `/compact`
|
|
427
|
+
> - **Explicit remember requests**: if you ask me to remember/save/store a long-term fact, I can use `memory_store` to commit it immediately
|
|
428
|
+
> - **On `/compact`**: pending session messages are extracted into long-term memories on the OpenViking server
|
|
427
429
|
> - **Future sessions**: relevant memories are auto-retrieved and injected before my replies
|
|
428
430
|
>
|
|
429
|
-
> Want to verify?
|
|
431
|
+
> Want to verify? Say "remember my email is test@example.com" so I can commit it via `memory_store`, or tell me a few memory-worthy facts and run `/compact`. Then start a new chat and ask "what's my email?" — it should recall.
|
|
430
432
|
|
|
431
433
|
---
|
|
432
434
|
|
|
@@ -434,15 +436,25 @@ Expected output:
|
|
|
434
436
|
|
|
435
437
|
## How It Works
|
|
436
438
|
|
|
437
|
-
The context-engine pipeline has three distinct stages — keep them apart, especially when telling users when memories become searchable long-term memory on the OpenViking server:
|
|
439
|
+
The context-engine pipeline has three distinct stages plus one explicit write path — keep them apart, especially when telling users when memories become searchable long-term memory on the OpenViking server:
|
|
438
440
|
|
|
439
|
-
- **Archive / capture (context-engine `afterTurn`)**: at the end of a user turn, the plugin
|
|
440
|
-
- **Memory extraction (
|
|
441
|
+
- **Archive / capture (context-engine `afterTurn`)**: at the end of a user turn, the plugin appends user/assistant messages to the OpenViking session via `POST /api/v1/sessions/.../messages`. This is **session capture only** unless `pending_tokens` crosses `commitTokenThreshold`; below the threshold, no memory extraction runs yet. You'll see session message counts grow on the server, but no new files under `viking://user/.../memories/`.
|
|
442
|
+
- **Memory extraction (threshold commit or `/compact`)**: memory extraction runs after a session commit. The commit can be triggered asynchronously when `afterTurn` crosses `commitTokenThreshold`, synchronously when the user invokes OpenClaw's `/compact` command, or explicitly by `memory_store`. The server-side extraction pipeline reads the archived session and writes new memories.
|
|
441
443
|
- `captureMode: "semantic"` (default): server extraction pipeline filters all qualifying text.
|
|
442
444
|
- `captureMode: "keyword"`: only text matching trigger words (e.g. "remember", "preference") is considered.
|
|
443
445
|
- **Auto-Recall (context-engine `assemble()`)**: before prompt context is assembled, the plugin queries OpenViking for relevant memories and injects them into context. Recall works even when there are no extracted memories yet — you just won't see anything come back.
|
|
444
446
|
|
|
445
|
-
**Practical implication for testing**: if you write down a fact and immediately try to recall it without `/compact`, the plugin
|
|
447
|
+
**Practical implication for testing**: if you write down a short fact and immediately try to recall it without a threshold commit, `/compact`, or `memory_store`, the plugin may only retrieve it as recent session context, not as a long-term memory. To verify long-term memory cross-session deterministically, run `/compact` or use `memory_store` for the fact being tested.
|
|
448
|
+
|
|
449
|
+
### Explicit long-term memory writes
|
|
450
|
+
|
|
451
|
+
Auto-capture is best-effort and commit-dependent. When the user explicitly says to remember, save, or store an important long-term fact, preference, project, or decision, the agent should call `memory_store` instead of waiting for ordinary auto-capture.
|
|
452
|
+
|
|
453
|
+
Use `memory_store` as the integration-side reliable path for durable-memory intent:
|
|
454
|
+
|
|
455
|
+
- It writes the supplied text into an OpenViking session and calls `commit(wait=true)`.
|
|
456
|
+
- It complements auto-capture; it does not replace normal session capture.
|
|
457
|
+
- If it commits but extracts 0 memories, the explicit path has done its job. Treat that as a server-side extraction/model/configuration issue and check OpenViking logs.
|
|
446
458
|
|
|
447
459
|
## Available Tools
|
|
448
460
|
|
|
@@ -468,6 +480,8 @@ Example: user asks "What programming language did I say I like?"
|
|
|
468
480
|
| `role` | No | Session role (default `user`) |
|
|
469
481
|
| `sessionId` | No | Existing OpenViking session ID |
|
|
470
482
|
|
|
483
|
+
Use this when the user explicitly asks to remember/save/store a long-term fact, preference, project, or decision.
|
|
484
|
+
|
|
471
485
|
Example: user says "Remember my email is xxx@example.com".
|
|
472
486
|
|
|
473
487
|
### `memory_forget` — Delete Memories
|
|
@@ -578,7 +592,7 @@ These are the keys under `plugins.entries.openviking.config` in `openclaw.json`.
|
|
|
578
592
|
| `userId` | — | Required when `apiKey` is a root key. |
|
|
579
593
|
| `targetUri` | `viking://user/memories` | Default search scope URI. |
|
|
580
594
|
| `timeoutMs` | (plugin default) | HTTP timeout for OpenViking calls. |
|
|
581
|
-
| `autoCapture` | `true` | Auto-
|
|
595
|
+
| `autoCapture` | `true` | Auto-append turn messages to the OpenViking session at `afterTurn`; extraction runs only after a threshold commit, `/compact`, or explicit `memory_store`. |
|
|
582
596
|
| `captureMode` | `"semantic"` | Filter mode used by the server-side extraction pipeline: `semantic` or `keyword`. |
|
|
583
597
|
| `captureMaxLength` | `24000` | Max text length per archived turn. |
|
|
584
598
|
| `autoRecall` | `true` | Auto-recall and inject memories before reply. |
|
|
@@ -589,7 +603,7 @@ These are the keys under `plugins.entries.openviking.config` in `openclaw.json`.
|
|
|
589
603
|
| `recallMaxInjectedChars` | `4000` | Hard cap on injected character count. Complete memories that do not fit are skipped, not truncated. |
|
|
590
604
|
| `recallPreferAbstract` | (plugin default) | Prefer abstract memories over raw. |
|
|
591
605
|
| `recallTokenBudget` | deprecated | Compatibility alias for `recallMaxInjectedChars`. |
|
|
592
|
-
| `
|
|
606
|
+
| `commitTokenThresholdRatio` | `0.5` | Async-commit threshold as a fraction (0-1) of the model context window (e.g. 0.5 = 50%); `0` commits every turn. |
|
|
593
607
|
| `commitKeepRecentCount` | `10` | Recent messages kept live after afterTurn commit. Compact always uses `0`. |
|
|
594
608
|
| `bypassSessionPatterns` | — | Glob patterns for sessions skipped by capture. |
|
|
595
609
|
| `ingestReplyAssist` | (plugin default) | Reply-assist ingestion toggle. |
|
|
@@ -722,7 +736,7 @@ Match against actual stderr / JSON `error` strings.
|
|
|
722
736
|
|
|
723
737
|
| Symptom (exact string) | Likely cause | Fix |
|
|
724
738
|
|---|---|---|
|
|
725
|
-
| `command not found: openclaw` | OpenClaw not installed | Stop. Ask user to install OpenClaw >= 2026.
|
|
739
|
+
| `command not found: openclaw` | OpenClaw not installed | Stop. Ask user to install OpenClaw >= 2026.5.27. |
|
|
726
740
|
| `command not found: node` / `'node' is not recognized` | Node missing | Stop. Ask user to install Node.js >= 22. |
|
|
727
741
|
| `429` / `rate limit` / `Too Many Requests` from `plugins install` | ClawHub throttle | Switch to backup Path B (`ov-install`). |
|
|
728
742
|
| `not logged in` / `please log in` from `plugins install` | Anonymous user hit limit | Switch to backup Path B (`ov-install`). |
|
|
@@ -42,7 +42,7 @@ OpenClaw owns agent execution, prompts, and tool invocation. OpenViking owns lon
|
|
|
42
42
|
| Layer | Current behavior |
|
|
43
43
|
|---|---|
|
|
44
44
|
| `assemble` | Rebuilds compressed session history from OpenViking and injects relevant recall into the latest user message. |
|
|
45
|
-
| `afterTurn` | Appends only the new turn to the OpenViking session; may trigger async commit when `pending_tokens >=
|
|
45
|
+
| `afterTurn` | Appends only the new turn to the OpenViking session; may trigger async commit when `pending_tokens >= tokenBudget * commitTokenThresholdRatio`. |
|
|
46
46
|
| `compact` | Runs `commit(wait=true)`, waits for archive/extraction completion, and reads back latest archive overview. |
|
|
47
47
|
| Tools | Memory recall/store/forget, archive search/expand, resource/skill import/search, recall trace query, tool-result list/search/read. |
|
|
48
48
|
|
|
@@ -77,7 +77,7 @@ Core config lives under `plugins.entries.openviking.config`:
|
|
|
77
77
|
| `recallLimit` | `6` | Max selected recall items. |
|
|
78
78
|
| `recallScoreThreshold` | `0.15` | Min score after post-processing. |
|
|
79
79
|
| `recallMaxInjectedChars` | `4000` | Total injected character cap; complete memories that do not fit are skipped. |
|
|
80
|
-
| `
|
|
80
|
+
| `commitTokenThresholdRatio` | `0.5` | Async-commit threshold as a fraction (0-1) of the model context window (e.g. 0.5 = 50%); `0` commits every turn. |
|
|
81
81
|
| `commitKeepRecentCount` | `10` | Recent messages kept live after afterTurn commit. Compact always uses `0`. |
|
|
82
82
|
| `bypassSessionPatterns` | empty | Glob-like session keys that completely bypass OpenViking (`*` segment, `**` multi-segment). |
|
|
83
83
|
| `emitStandardDiagnostics` | `false` | Emit structured `openviking: diag {...}` lines. |
|
|
@@ -272,7 +272,7 @@ Command parsers support quoted args and flags. Resource-only flags are rejected
|
|
|
272
272
|
| `configured=false` | Setup did not persist config | Re-run `openclaw openviking setup ... --json`; branch on JSON `error`. |
|
|
273
273
|
| `slotActive=false` | Another context engine owns the slot or gateway has stale state | Inspect `plugins.slots.contextEngine`; use `--force-slot` only after user confirms. |
|
|
274
274
|
| `health.ok=false` | Server unreachable or wrong `baseUrl` / key | Check `baseUrl`, network, `/health`, and auth. |
|
|
275
|
-
| No long-term memory after a fresh fact | `/compact` or commit/extraction has not run, or server extraction failed |
|
|
275
|
+
| No long-term memory after a fresh fact | `/compact` or commit/extraction has not run, or server extraction failed | Use `memory_store` for explicit remember/save/store intents; otherwise run `/compact` or wait for threshold commit, then check OpenViking server logs. |
|
|
276
276
|
| Recall misses shared documents | `resource` target is not enabled | Use `memory_recall` with `resourceTypes:["resource"]` or configure `recallTargetTypes: ["resource"]`. |
|
|
277
277
|
| Summary lacks exact detail | Archive summary is too coarse | Use `ov_archive_search` with concrete keywords, then `ov_archive_expand`. |
|
|
278
278
|
| Large tool output preview is insufficient | Tool result was externalized | Use `openviking_tool_result_search/read` with the ref. |
|
package/text-utils.ts
CHANGED
|
@@ -44,14 +44,9 @@ function looksLikeMetadataJsonBlock(content: string): boolean {
|
|
|
44
44
|
return matchedKeys.size >= 3;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
const HEARTBEAT_RE = /\bHEARTBEAT(?:\.md|_OK)\b/;
|
|
48
47
|
const TOOL_PLACEHOLDER_RE = /^\s*\[tool(?::\s*|Use:\s*)[^\]]+\]\s*$/i;
|
|
49
48
|
|
|
50
49
|
export function sanitizeUserTextForCapture(text: string): string {
|
|
51
|
-
// 过滤 HEARTBEAT 健康检查消息
|
|
52
|
-
if (HEARTBEAT_RE.test(text)) {
|
|
53
|
-
return "";
|
|
54
|
-
}
|
|
55
50
|
// Drop legacy synthetic tool placeholders before they reach memory extraction.
|
|
56
51
|
if (TOOL_PLACEHOLDER_RE.test(text)) {
|
|
57
52
|
return "";
|
|
@@ -409,7 +404,7 @@ export function extractNewTurnMessages(
|
|
|
409
404
|
: undefined);
|
|
410
405
|
if (output) {
|
|
411
406
|
result.push({
|
|
412
|
-
role: "
|
|
407
|
+
role: "assistant",
|
|
413
408
|
parts: [{
|
|
414
409
|
type: "tool",
|
|
415
410
|
toolCallId: toolCallId || undefined,
|