@openviking/openclaw-plugin 2026.5.10-dev.3 → 2026.5.26
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 +355 -355
- package/INSTALL-ZH.md +345 -345
- package/INSTALL.md +362 -362
- package/README.md +338 -317
- package/auto-recall.ts +257 -257
- package/client.ts +939 -828
- package/commands/setup.ts +1002 -1002
- package/config.ts +476 -476
- package/context-engine.ts +1737 -1700
- package/dist/client.js +30 -0
- package/dist/context-engine.js +40 -8
- package/dist/index.js +313 -23
- package/dist/process-manager.js +0 -87
- package/dist/runtime-utils.js +0 -3
- package/dist/text-utils.js +31 -51
- package/images/openclaw-plugin-engine-overview.png +0 -0
- package/images/openclaw-plugin-recall-flow.png +0 -0
- package/images/openclaw-plugin-runtime-routing.png +0 -0
- package/images/openclaw-plugin-session-lifecycle.png +0 -0
- package/index.ts +1985 -1639
- package/install-manifest.json +47 -47
- package/memory-ranking.ts +267 -267
- package/openclaw.plugin.json +335 -332
- package/package.json +76 -75
- package/process-manager.ts +45 -169
- package/runtime-utils.ts +5 -8
- package/session-transcript-repair.ts +530 -530
- package/skills/install-openviking-memory/SKILL.md +665 -665
- package/text-utils.ts +507 -524
- package/tool-call-id.ts +331 -331
package/dist/process-manager.js
CHANGED
|
@@ -1,66 +1,3 @@
|
|
|
1
|
-
import { Socket } from "node:net";
|
|
2
|
-
export function waitForHealth(baseUrl, timeoutMs, intervalMs) {
|
|
3
|
-
const deadline = Date.now() + timeoutMs;
|
|
4
|
-
return new Promise((resolve, reject) => {
|
|
5
|
-
const tick = () => {
|
|
6
|
-
if (Date.now() > deadline) {
|
|
7
|
-
reject(new Error(`OpenViking health check timeout at ${baseUrl}`));
|
|
8
|
-
return;
|
|
9
|
-
}
|
|
10
|
-
fetch(`${baseUrl}/health`)
|
|
11
|
-
.then((r) => r.json())
|
|
12
|
-
.then((body) => {
|
|
13
|
-
if (body?.status === "ok") {
|
|
14
|
-
resolve();
|
|
15
|
-
return;
|
|
16
|
-
}
|
|
17
|
-
setTimeout(tick, intervalMs);
|
|
18
|
-
})
|
|
19
|
-
.catch(() => setTimeout(tick, intervalMs));
|
|
20
|
-
};
|
|
21
|
-
tick();
|
|
22
|
-
});
|
|
23
|
-
}
|
|
24
|
-
export function waitForHealthOrExit(baseUrl, timeoutMs, intervalMs, child) {
|
|
25
|
-
const exited = child.killed || child.exitCode !== null || child.signalCode !== null;
|
|
26
|
-
if (exited) {
|
|
27
|
-
return Promise.reject(new Error(`OpenViking subprocess exited before health check ` +
|
|
28
|
-
`(code=${child.exitCode}, signal=${child.signalCode})`));
|
|
29
|
-
}
|
|
30
|
-
return new Promise((resolve, reject) => {
|
|
31
|
-
let settled = false;
|
|
32
|
-
const cleanup = () => {
|
|
33
|
-
child.off?.("error", onError);
|
|
34
|
-
child.off?.("exit", onExit);
|
|
35
|
-
};
|
|
36
|
-
const finishResolve = () => {
|
|
37
|
-
if (settled) {
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
settled = true;
|
|
41
|
-
cleanup();
|
|
42
|
-
resolve();
|
|
43
|
-
};
|
|
44
|
-
const finishReject = (err) => {
|
|
45
|
-
if (settled) {
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
settled = true;
|
|
49
|
-
cleanup();
|
|
50
|
-
reject(err instanceof Error ? err : new Error(String(err)));
|
|
51
|
-
};
|
|
52
|
-
const onError = (err) => {
|
|
53
|
-
finishReject(err);
|
|
54
|
-
};
|
|
55
|
-
const onExit = (code, signal) => {
|
|
56
|
-
finishReject(new Error(`OpenViking subprocess exited before health check ` +
|
|
57
|
-
`(code=${code}, signal=${signal})`));
|
|
58
|
-
};
|
|
59
|
-
child.once("error", onError);
|
|
60
|
-
child.once("exit", onExit);
|
|
61
|
-
waitForHealth(baseUrl, timeoutMs, intervalMs).then(finishResolve, finishReject);
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
1
|
export function withTimeout(promise, timeoutMs, timeoutMessage) {
|
|
65
2
|
return new Promise((resolve, reject) => {
|
|
66
3
|
const timer = setTimeout(() => reject(new Error(timeoutMessage)), timeoutMs);
|
|
@@ -73,30 +10,6 @@ export function withTimeout(promise, timeoutMs, timeoutMessage) {
|
|
|
73
10
|
});
|
|
74
11
|
});
|
|
75
12
|
}
|
|
76
|
-
export function quickTcpProbe(host, port, timeoutMs) {
|
|
77
|
-
return new Promise((resolve) => {
|
|
78
|
-
const socket = new Socket();
|
|
79
|
-
let done = false;
|
|
80
|
-
const finish = (ok) => {
|
|
81
|
-
if (done) {
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
done = true;
|
|
85
|
-
socket.destroy();
|
|
86
|
-
resolve(ok);
|
|
87
|
-
};
|
|
88
|
-
socket.setTimeout(timeoutMs);
|
|
89
|
-
socket.once("connect", () => finish(true));
|
|
90
|
-
socket.once("timeout", () => finish(false));
|
|
91
|
-
socket.once("error", () => finish(false));
|
|
92
|
-
try {
|
|
93
|
-
socket.connect(port, host);
|
|
94
|
-
}
|
|
95
|
-
catch {
|
|
96
|
-
finish(false);
|
|
97
|
-
}
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
13
|
export async function quickHealthCheck(baseUrl, timeoutMs) {
|
|
101
14
|
const controller = new AbortController();
|
|
102
15
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
package/dist/runtime-utils.js
CHANGED
package/dist/text-utils.js
CHANGED
|
@@ -21,7 +21,6 @@ const NON_CONTENT_TEXT_RE = /^[\p{P}\p{S}\s]+$/u;
|
|
|
21
21
|
const SUBAGENT_CONTEXT_RE = /^\s*\[Subagent Context\]/i;
|
|
22
22
|
const MEMORY_INTENT_RE = /记住|记下|remember|save|store|偏好|preference|规则|rule|事实|fact/i;
|
|
23
23
|
const QUESTION_CUE_RE = /[??]|\b(?:what|when|where|who|why|how|which|can|could|would|did|does|is|are)\b|^(?:请问|能否|可否|怎么|如何|什么时候|谁|什么|哪|是否)/i;
|
|
24
|
-
export const CAPTURE_LIMIT = 3;
|
|
25
24
|
function resolveCaptureMinLength(text) {
|
|
26
25
|
return CJK_CHAR_REGEX.test(text) ? 4 : 10;
|
|
27
26
|
}
|
|
@@ -232,21 +231,6 @@ export function extractTextsFromUserMessages(messages) {
|
|
|
232
231
|
}
|
|
233
232
|
return texts;
|
|
234
233
|
}
|
|
235
|
-
function formatToolUseBlock(b) {
|
|
236
|
-
const name = typeof b.name === "string" ? b.name : "unknown";
|
|
237
|
-
let inputStr = "";
|
|
238
|
-
if (b.input !== undefined && b.input !== null) {
|
|
239
|
-
try {
|
|
240
|
-
inputStr = typeof b.input === "string" ? b.input : JSON.stringify(b.input);
|
|
241
|
-
}
|
|
242
|
-
catch {
|
|
243
|
-
inputStr = String(b.input);
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
return inputStr
|
|
247
|
-
? `[toolUse: ${name}]\n${inputStr}`
|
|
248
|
-
: `[toolUse: ${name}]`;
|
|
249
|
-
}
|
|
250
234
|
function formatToolResultContent(content) {
|
|
251
235
|
if (typeof content === "string")
|
|
252
236
|
return content.trim();
|
|
@@ -270,40 +254,6 @@ function formatToolResultContent(content) {
|
|
|
270
254
|
}
|
|
271
255
|
return "";
|
|
272
256
|
}
|
|
273
|
-
/**
|
|
274
|
-
* Extract text from a single message without a `[role]:` prefix.
|
|
275
|
-
* Used by afterTurn to send messages with their actual role.
|
|
276
|
-
*/
|
|
277
|
-
export function extractSingleMessageText(msg) {
|
|
278
|
-
if (!msg || typeof msg !== "object")
|
|
279
|
-
return "";
|
|
280
|
-
const m = msg;
|
|
281
|
-
const role = m.role;
|
|
282
|
-
if (!role || role === "system")
|
|
283
|
-
return "";
|
|
284
|
-
if (role === "toolResult") {
|
|
285
|
-
const toolName = typeof m.toolName === "string" ? m.toolName : "tool";
|
|
286
|
-
const resultText = formatToolResultContent(m.content);
|
|
287
|
-
return resultText ? `[${toolName} result]: ${resultText}` : "";
|
|
288
|
-
}
|
|
289
|
-
const content = m.content;
|
|
290
|
-
if (typeof content === "string")
|
|
291
|
-
return content.trim();
|
|
292
|
-
if (Array.isArray(content)) {
|
|
293
|
-
const parts = [];
|
|
294
|
-
for (const block of content) {
|
|
295
|
-
const b = block;
|
|
296
|
-
if (b?.type === "text" && typeof b.text === "string") {
|
|
297
|
-
parts.push(b.text.trim());
|
|
298
|
-
}
|
|
299
|
-
else if (b?.type === "toolUse") {
|
|
300
|
-
parts.push(formatToolUseBlock(b));
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
return parts.join("\n");
|
|
304
|
-
}
|
|
305
|
-
return "";
|
|
306
|
-
}
|
|
307
257
|
/**
|
|
308
258
|
* 提取消息中的一个 part 的文本内容,并清理时间戳等噪音
|
|
309
259
|
*/
|
|
@@ -394,7 +344,6 @@ export function extractNewTurnMessages(messages, startIndex) {
|
|
|
394
344
|
continue;
|
|
395
345
|
}
|
|
396
346
|
// user/assistant -> type: "text"
|
|
397
|
-
// 统一 role 为 user
|
|
398
347
|
const content = msg.content;
|
|
399
348
|
const text = extractPartText(content);
|
|
400
349
|
if (text) {
|
|
@@ -412,6 +361,37 @@ export function extractNewTurnMessages(messages, startIndex) {
|
|
|
412
361
|
});
|
|
413
362
|
}
|
|
414
363
|
}
|
|
364
|
+
else {
|
|
365
|
+
/// 如果原始消息有 toolCall,提取所有工具名并生成占位符
|
|
366
|
+
if (role === "assistant" && Array.isArray(content)) {
|
|
367
|
+
const toolNames = [];
|
|
368
|
+
// 收集所有 toolCall 的 tool_name
|
|
369
|
+
for (const block of content) {
|
|
370
|
+
const b = block;
|
|
371
|
+
const blockType = b?.type;
|
|
372
|
+
if (blockType === "toolCall" ||
|
|
373
|
+
blockType === "toolUse" ||
|
|
374
|
+
blockType === "tool_use" ||
|
|
375
|
+
blockType === "tool_call") {
|
|
376
|
+
const name = b?.name || b?.toolName;
|
|
377
|
+
if (name && typeof name === "string" && name.trim()) {
|
|
378
|
+
toolNames.push(name.trim());
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
// 只有找到 toolCall 时才添加占位符
|
|
383
|
+
if (toolNames.length > 0) {
|
|
384
|
+
const toolNamesStr = toolNames.join(", ");
|
|
385
|
+
result.push({
|
|
386
|
+
role: "assistant",
|
|
387
|
+
parts: [{
|
|
388
|
+
type: "text",
|
|
389
|
+
text: `[tool: ${toolNamesStr}]`,
|
|
390
|
+
}],
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
415
395
|
}
|
|
416
396
|
return { messages: result, newCount: count };
|
|
417
397
|
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|