@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.
- package/INSTALL-AGENT.md +355 -355
- package/INSTALL-ZH.md +405 -345
- package/INSTALL.md +366 -362
- package/README.md +364 -338
- package/adapters/http-transport.ts +3 -0
- package/adapters/resource-packager.ts +141 -0
- package/auto-recall.ts +518 -257
- package/client.ts +963 -939
- package/commands/setup.ts +1169 -1002
- package/config/feature-gates.json +10 -0
- package/config.ts +819 -476
- package/context-engine.ts +408 -1737
- package/dist/adapters/http-transport.js +1 -0
- package/dist/adapters/resource-packager.js +114 -0
- package/dist/auto-recall.js +228 -32
- package/dist/client.js +125 -161
- package/dist/commands/setup.js +170 -32
- package/dist/config.js +285 -64
- package/dist/context-engine.js +64 -1179
- package/dist/index.js +209 -1470
- 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 +5 -17
- 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/dist/token-estimator.js +51 -0
- package/index.ts +408 -1985
- package/install-manifest.json +57 -47
- package/memory-ranking.ts +303 -267
- package/openclaw.plugin.json +497 -335
- package/package.json +86 -76
- 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 +41 -45
- 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/runtime-utils.ts +5 -5
- 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 +528 -530
- package/skills/install-openviking-memory/SKILL.md +752 -665
- package/skills/openviking-context-database/SKILL.md +286 -0
- package/text-utils.ts +488 -507
- package/token-estimator.ts +65 -0
- package/tool-call-id.ts +331 -331
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const defaultHttpTransport = (url, init) => fetch(url, init);
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { once } from "node:events";
|
|
3
|
+
import { createWriteStream } from "node:fs";
|
|
4
|
+
import { mkdtemp, readdir, readFile, rm, stat } from "node:fs/promises";
|
|
5
|
+
import { tmpdir } from "node:os";
|
|
6
|
+
import { basename, dirname, join, relative } from "node:path";
|
|
7
|
+
import { Zip, ZipDeflate } from "fflate";
|
|
8
|
+
const REMOTE_RESOURCE_PREFIXES = ["http://", "https://", "git@", "ssh://", "git://"];
|
|
9
|
+
function toBlobPart(value) {
|
|
10
|
+
return value.buffer.slice(value.byteOffset, value.byteOffset + value.byteLength);
|
|
11
|
+
}
|
|
12
|
+
export function isRemoteResourceSource(source) {
|
|
13
|
+
return REMOTE_RESOURCE_PREFIXES.some((prefix) => source.startsWith(prefix));
|
|
14
|
+
}
|
|
15
|
+
export async function cleanupUploadTempPath(path) {
|
|
16
|
+
if (!path) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
await rm(path, { force: true }).catch(() => undefined);
|
|
20
|
+
await rm(dirname(path), { recursive: true, force: true }).catch(() => undefined);
|
|
21
|
+
}
|
|
22
|
+
export async function zipDirectoryForUpload(dirPath) {
|
|
23
|
+
const rootStats = await stat(dirPath);
|
|
24
|
+
if (!rootStats.isDirectory()) {
|
|
25
|
+
throw new Error(`Not a directory: ${dirPath}`);
|
|
26
|
+
}
|
|
27
|
+
const zipDir = await mkdtemp(join(tmpdir(), "openviking-openclaw-upload-"));
|
|
28
|
+
const zipPath = join(zipDir, `${basename(dirPath).replace(/[^a-zA-Z0-9._-]/g, "_")}-${randomUUID()}.zip`);
|
|
29
|
+
const output = createWriteStream(zipPath);
|
|
30
|
+
const outputClosed = once(output, "close");
|
|
31
|
+
const outputErrored = once(output, "error").then(([err]) => Promise.reject(err));
|
|
32
|
+
const zip = new Zip((err, chunk, final) => {
|
|
33
|
+
if (err) {
|
|
34
|
+
output.destroy(err);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
if (chunk?.length) {
|
|
38
|
+
output.write(Buffer.from(chunk));
|
|
39
|
+
}
|
|
40
|
+
if (final) {
|
|
41
|
+
output.end();
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
const walk = async (currentDir) => {
|
|
45
|
+
const entries = await readdir(currentDir, { withFileTypes: true });
|
|
46
|
+
for (const entry of entries) {
|
|
47
|
+
const fullPath = join(currentDir, entry.name);
|
|
48
|
+
if (entry.isDirectory()) {
|
|
49
|
+
await walk(fullPath);
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
if (!entry.isFile()) {
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
const relPath = relative(dirPath, fullPath).split("\\").join("/");
|
|
56
|
+
if (!relPath || relPath.startsWith("../") || relPath.includes("/../")) {
|
|
57
|
+
throw new Error(`Unsafe relative path while zipping: ${relPath}`);
|
|
58
|
+
}
|
|
59
|
+
const file = new ZipDeflate(relPath);
|
|
60
|
+
zip.add(file);
|
|
61
|
+
file.push(new Uint8Array(await readFile(fullPath)), true);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
try {
|
|
65
|
+
await walk(dirPath);
|
|
66
|
+
zip.end();
|
|
67
|
+
await Promise.race([outputClosed, outputErrored]);
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
zip.terminate();
|
|
71
|
+
output.destroy(err);
|
|
72
|
+
await cleanupUploadTempPath(zipPath);
|
|
73
|
+
throw err;
|
|
74
|
+
}
|
|
75
|
+
return zipPath;
|
|
76
|
+
}
|
|
77
|
+
async function prepareLocalUploadSource(path, includeSourceName = false) {
|
|
78
|
+
const localStats = await stat(path);
|
|
79
|
+
if (localStats.isDirectory()) {
|
|
80
|
+
const uploadPath = await zipDirectoryForUpload(path);
|
|
81
|
+
return {
|
|
82
|
+
kind: "upload",
|
|
83
|
+
uploadPath,
|
|
84
|
+
cleanupPath: uploadPath,
|
|
85
|
+
...(includeSourceName ? { sourceName: basename(path) } : {}),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
if (!localStats.isFile()) {
|
|
89
|
+
throw new Error(`Path is not a file or directory: ${path}`);
|
|
90
|
+
}
|
|
91
|
+
return { kind: "upload", uploadPath: path };
|
|
92
|
+
}
|
|
93
|
+
export const defaultResourcePackager = {
|
|
94
|
+
async prepareResourceSource(pathOrUrl) {
|
|
95
|
+
if (isRemoteResourceSource(pathOrUrl)) {
|
|
96
|
+
return { kind: "remote", path: pathOrUrl };
|
|
97
|
+
}
|
|
98
|
+
return prepareLocalUploadSource(pathOrUrl, true);
|
|
99
|
+
},
|
|
100
|
+
async prepareLocalUploadSource(path) {
|
|
101
|
+
return prepareLocalUploadSource(path, false);
|
|
102
|
+
},
|
|
103
|
+
async createTempUploadBody(uploadPath) {
|
|
104
|
+
const fileBytes = await readFile(uploadPath);
|
|
105
|
+
const form = new FormData();
|
|
106
|
+
form.append("file", new Blob([toBlobPart(fileBytes)], { type: "application/octet-stream" }), basename(uploadPath));
|
|
107
|
+
return form;
|
|
108
|
+
},
|
|
109
|
+
async cleanup(source) {
|
|
110
|
+
if (source?.kind === "upload") {
|
|
111
|
+
await cleanupUploadTempPath(source.cleanupPath);
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
};
|
package/dist/auto-recall.js
CHANGED
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
import { pickMemoriesForInjection, postProcessMemories, summarizeInjectionMemories, toJsonLog, } from "./memory-ranking.js";
|
|
2
2
|
import { quickRecallPrecheck, withTimeout } from "./process-manager.js";
|
|
3
|
+
import { resolveRecallSearchPlan, } from "./registries/recall-resource-types.js";
|
|
3
4
|
import { sanitizeUserTextForCapture } from "./text-utils.js";
|
|
5
|
+
import { estimateTextTokens } from "./token-estimator.js";
|
|
4
6
|
const AUTO_RECALL_TIMEOUT_MS = 5_000;
|
|
5
7
|
const RECALL_QUERY_MAX_CHARS = 4_000;
|
|
6
8
|
export const AUTO_RECALL_SOURCE_MARKER = "Source: openviking-auto-recall";
|
|
9
|
+
const WRITE_OR_EFFECT_RE = /\b(write|edit|modify|delete|remove|migrate|deploy|release|publish|configure|patch)\b|写|改|修改|删除|迁移|部署|发布|配置|打补丁/i;
|
|
10
|
+
const EXECUTION_RE = /\b(fix|debug|test|build|run|implement|refactor|integrate|repair|troubleshoot)\b|修复|调试|测试|构建|运行|实现|重构|对接|排查/i;
|
|
11
|
+
const FAILURE_RE = /\b(error|exception|traceback|failed|failure|retry|exit code|test failed)\b|报错|异常|失败|重试|挂了|不通过/i;
|
|
12
|
+
const ENGINEERING_OBJECT_RE = /(?:^|\s)(?:[\w.-]+\/[\w./-]+|[\w./-]+\.(?:ts|tsx|js|jsx|py|go|rs|java|md|json|ya?ml|toml|sh|sql))\b|`[^`]+`|\b(?:repo|workspace|plugin|service|component|hook|api|tool|package|module)\b|仓库|工作区|插件|服务|组件|接口|工具|模块|文件/i;
|
|
13
|
+
const EXPERIENCE_INTENT_RE = /经验|踩坑|最佳实践|不要再|按之前|avoid|best practice|lesson|pitfall/i;
|
|
14
|
+
const QUESTION_ONLY_RE = /^(?:什么是|是什么|区别|解释|讲讲|怎么看|为什么|如何理解|where is|what is|explain|difference between)\b|[??]$/i;
|
|
15
|
+
const CASUAL_RE = /闲聊|翻译|总结当前对话|天气|笑话|hello|hi\b|你好/i;
|
|
7
16
|
export function prepareRecallQuery(rawText) {
|
|
8
17
|
const sanitized = sanitizeUserTextForCapture(rawText).trim();
|
|
9
18
|
const originalChars = sanitized.length;
|
|
@@ -25,11 +34,29 @@ export function prepareRecallQuery(rawText) {
|
|
|
25
34
|
finalChars: query.length,
|
|
26
35
|
};
|
|
27
36
|
}
|
|
28
|
-
/** Estimate token count using
|
|
37
|
+
/** Estimate token count using the shared CJK-aware fallback for diagnostics. */
|
|
29
38
|
export function estimateTokenCount(text) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
39
|
+
return estimateTextTokens(text);
|
|
40
|
+
}
|
|
41
|
+
function memoryCategory(item) {
|
|
42
|
+
return item.category?.trim() || "memory";
|
|
43
|
+
}
|
|
44
|
+
function indentContent(content) {
|
|
45
|
+
return content
|
|
46
|
+
.split("\n")
|
|
47
|
+
.map((line) => ` ${line}`)
|
|
48
|
+
.join("\n");
|
|
49
|
+
}
|
|
50
|
+
function formatMemoryLine(item, content, options) {
|
|
51
|
+
const category = memoryCategory(item);
|
|
52
|
+
if (!options.includeUri) {
|
|
53
|
+
return `- [${category}] ${content}`;
|
|
54
|
+
}
|
|
55
|
+
return [
|
|
56
|
+
`- [${category}]`,
|
|
57
|
+
` <uri>${item.uri}</uri>`,
|
|
58
|
+
indentContent(content),
|
|
59
|
+
].join("\n");
|
|
33
60
|
}
|
|
34
61
|
async function resolveMemoryContent(item, readFn, options) {
|
|
35
62
|
let content;
|
|
@@ -57,7 +84,7 @@ export async function buildMemoryLines(memories, readFn, options) {
|
|
|
57
84
|
const lines = [];
|
|
58
85
|
for (const item of memories) {
|
|
59
86
|
const content = await resolveMemoryContent(item, readFn, options);
|
|
60
|
-
lines.push(
|
|
87
|
+
lines.push(formatMemoryLine(item, content, options));
|
|
61
88
|
}
|
|
62
89
|
return lines;
|
|
63
90
|
}
|
|
@@ -78,7 +105,7 @@ export async function buildMemoryLinesWithBudget(memories, readFn, options) {
|
|
|
78
105
|
break;
|
|
79
106
|
}
|
|
80
107
|
const content = await resolveMemoryContent(item, readFn, options);
|
|
81
|
-
const line =
|
|
108
|
+
const line = formatMemoryLine(item, content, options);
|
|
82
109
|
const separatorChars = lines.length > 0 ? 1 : 0;
|
|
83
110
|
const projectedChars = totalChars + separatorChars + line.length;
|
|
84
111
|
if (projectedChars > charBudget) {
|
|
@@ -100,68 +127,237 @@ export function buildRecallContextBlock(memoryLines) {
|
|
|
100
127
|
"</relevant-memories>",
|
|
101
128
|
].join("\n");
|
|
102
129
|
}
|
|
130
|
+
function newTraceId() {
|
|
131
|
+
return `recall_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
|
|
132
|
+
}
|
|
133
|
+
function preview(value, maxChars) {
|
|
134
|
+
const trimmed = value?.trim();
|
|
135
|
+
if (!trimmed)
|
|
136
|
+
return undefined;
|
|
137
|
+
return trimmed.length > maxChars ? trimmed.slice(0, maxChars) : trimmed;
|
|
138
|
+
}
|
|
139
|
+
function toTraceResults(items, resourceType) {
|
|
140
|
+
return items.map((item) => ({
|
|
141
|
+
uri: item.uri,
|
|
142
|
+
resourceType,
|
|
143
|
+
category: item.category,
|
|
144
|
+
score: item.score,
|
|
145
|
+
level: item.level,
|
|
146
|
+
abstractPreview: preview(item.abstract ?? item.overview, 240),
|
|
147
|
+
resultType: resourceType === "resource" ? "resource" : "memory",
|
|
148
|
+
}));
|
|
149
|
+
}
|
|
150
|
+
function boundTraceQuery(query, maxChars) {
|
|
151
|
+
if (query.length <= maxChars) {
|
|
152
|
+
return { query };
|
|
153
|
+
}
|
|
154
|
+
return { query: query.slice(0, maxChars), queryTruncated: true };
|
|
155
|
+
}
|
|
156
|
+
function runtimeFlag(runtimeContext, key) {
|
|
157
|
+
return runtimeContext && typeof runtimeContext === "object"
|
|
158
|
+
? runtimeContext[key]
|
|
159
|
+
: undefined;
|
|
160
|
+
}
|
|
161
|
+
export function isCronSession(sessionKey, runtimeContext) {
|
|
162
|
+
return Boolean(sessionKey?.includes(":cron:") ||
|
|
163
|
+
runtimeFlag(runtimeContext, "isCron") === true ||
|
|
164
|
+
runtimeFlag(runtimeContext, "automationKind") === "cron");
|
|
165
|
+
}
|
|
166
|
+
export function shouldRecallAgentExperience(input) {
|
|
167
|
+
const text = sanitizeUserTextForCapture(input.latestUserText).trim();
|
|
168
|
+
const minQueryChars = input.minQueryChars ?? 12;
|
|
169
|
+
if (input.isBypassed) {
|
|
170
|
+
return { recall: false, score: 0, reason: "session_bypassed" };
|
|
171
|
+
}
|
|
172
|
+
if (!text || text.length < minQueryChars) {
|
|
173
|
+
return { recall: false, score: 0, reason: "query_too_short" };
|
|
174
|
+
}
|
|
175
|
+
if (/<openviking-context\b/i.test(input.latestUserText)) {
|
|
176
|
+
return { recall: false, score: 0, reason: "already_injected" };
|
|
177
|
+
}
|
|
178
|
+
const trigger = input.triggerHint ?? (isCronSession(input.sessionKey, input.runtimeContext) ? "cron_start" : "task_start");
|
|
179
|
+
if (trigger !== "task_start") {
|
|
180
|
+
return { recall: true, trigger, score: 99, reason: "forced_trigger" };
|
|
181
|
+
}
|
|
182
|
+
let score = 0;
|
|
183
|
+
if (WRITE_OR_EFFECT_RE.test(text))
|
|
184
|
+
score += 3;
|
|
185
|
+
if (EXECUTION_RE.test(text))
|
|
186
|
+
score += 2;
|
|
187
|
+
if (FAILURE_RE.test(text))
|
|
188
|
+
score += 2;
|
|
189
|
+
if (ENGINEERING_OBJECT_RE.test(text))
|
|
190
|
+
score += 2;
|
|
191
|
+
if (EXPERIENCE_INTENT_RE.test(text))
|
|
192
|
+
score += 1;
|
|
193
|
+
if (CASUAL_RE.test(text))
|
|
194
|
+
score -= 3;
|
|
195
|
+
if (QUESTION_ONLY_RE.test(text) && !ENGINEERING_OBJECT_RE.test(text) && !EXECUTION_RE.test(text)) {
|
|
196
|
+
score -= 2;
|
|
197
|
+
}
|
|
198
|
+
if (score >= 3) {
|
|
199
|
+
return { recall: true, trigger: "task_start", score, reason: "task_execution" };
|
|
200
|
+
}
|
|
201
|
+
return { recall: false, score, reason: score < 0 ? "non_execution" : "below_threshold" };
|
|
202
|
+
}
|
|
103
203
|
export async function buildAutoRecallContext(params) {
|
|
104
204
|
const { cfg, client, agentId, queryText, logger, verbose } = params;
|
|
205
|
+
const queryConfig = params.queryConfig;
|
|
105
206
|
if (!cfg.autoRecall || queryText.length < 5) {
|
|
106
207
|
return { memoryCount: 0, estimatedTokens: 0 };
|
|
107
208
|
}
|
|
108
|
-
const precheck = await quickRecallPrecheck(
|
|
209
|
+
const precheck = await quickRecallPrecheck(client, agentId);
|
|
109
210
|
if (!precheck.ok) {
|
|
110
211
|
verbose?.(`openviking: skipping auto-recall because precheck failed (${precheck.reason})`);
|
|
111
212
|
return { memoryCount: 0, estimatedTokens: 0 };
|
|
112
213
|
}
|
|
113
214
|
return withTimeout((async () => {
|
|
114
|
-
const candidateLimit = Math.max(cfg.recallLimit * 4, 20);
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
215
|
+
const candidateLimit = queryConfig?.candidateLimit ?? Math.max(cfg.recallLimit * 4, 20);
|
|
216
|
+
const scoreThreshold = queryConfig?.scoreThreshold ?? cfg.recallScoreThreshold;
|
|
217
|
+
const recallLimit = queryConfig?.recallLimit ?? cfg.recallLimit;
|
|
218
|
+
const maxInjectedChars = queryConfig?.maxInjectedChars ?? cfg.recallMaxInjectedChars;
|
|
219
|
+
const recallPreferAbstract = queryConfig?.recallPreferAbstract ?? cfg.recallPreferAbstract;
|
|
220
|
+
const searchPlan = resolveRecallSearchPlan(params.resourceTypes ?? queryConfig?.resourceTypes ?? cfg.recallTargetTypes, {
|
|
221
|
+
ovSessionId: params.ovSessionId,
|
|
222
|
+
agentId,
|
|
223
|
+
});
|
|
224
|
+
const traceSearches = searchPlan.skipped.map((skipped) => ({
|
|
225
|
+
resourceType: skipped.resourceType,
|
|
226
|
+
limit: candidateLimit,
|
|
227
|
+
scoreThreshold: 0,
|
|
228
|
+
durationMs: 0,
|
|
229
|
+
total: 0,
|
|
230
|
+
results: [],
|
|
231
|
+
error: skipped.reason,
|
|
232
|
+
}));
|
|
233
|
+
const autoRecallPromises = searchPlan.searches.map(async (search) => {
|
|
234
|
+
const start = Date.now();
|
|
235
|
+
const result = await client.find(queryText, {
|
|
236
|
+
targetUri: search.targetUri,
|
|
130
237
|
limit: candidateLimit,
|
|
131
238
|
scoreThreshold: 0,
|
|
132
|
-
|
|
133
|
-
|
|
239
|
+
contextType: search.contextType,
|
|
240
|
+
actorPeerId: agentId,
|
|
241
|
+
});
|
|
242
|
+
return {
|
|
243
|
+
resourceType: search.resourceType,
|
|
244
|
+
targetUri: search.targetUri,
|
|
245
|
+
result,
|
|
246
|
+
durationMs: Date.now() - start,
|
|
247
|
+
};
|
|
248
|
+
});
|
|
134
249
|
const autoRecallSettled = await Promise.allSettled(autoRecallPromises);
|
|
135
250
|
const allMemories = [];
|
|
136
251
|
for (const s of autoRecallSettled) {
|
|
137
252
|
if (s.status === "fulfilled") {
|
|
138
|
-
|
|
253
|
+
const result = s.value.result ?? {};
|
|
254
|
+
const memories = result.memories ?? [];
|
|
255
|
+
const resources = result.resources ?? [];
|
|
256
|
+
allMemories.push(...memories, ...resources);
|
|
257
|
+
traceSearches.push({
|
|
258
|
+
resourceType: s.value.resourceType,
|
|
259
|
+
targetUriInput: s.value.targetUri,
|
|
260
|
+
limit: candidateLimit,
|
|
261
|
+
scoreThreshold: 0,
|
|
262
|
+
durationMs: s.value.durationMs,
|
|
263
|
+
total: result.total ?? memories.length + resources.length + (result.skills?.length ?? 0),
|
|
264
|
+
results: [
|
|
265
|
+
...toTraceResults(memories, s.value.resourceType),
|
|
266
|
+
...toTraceResults(resources, "resource"),
|
|
267
|
+
...(result.skills ?? []).map((item) => ({
|
|
268
|
+
uri: item.uri,
|
|
269
|
+
resourceType: s.value.resourceType,
|
|
270
|
+
category: item.category,
|
|
271
|
+
score: item.score,
|
|
272
|
+
level: item.level,
|
|
273
|
+
abstractPreview: preview(item.abstract ?? item.overview, 240),
|
|
274
|
+
resultType: "skill",
|
|
275
|
+
})),
|
|
276
|
+
].slice(0, cfg.traceRecallMaxResultsPerSearch),
|
|
277
|
+
});
|
|
139
278
|
}
|
|
140
279
|
else {
|
|
141
280
|
logger.warn?.(`openviking: auto-recall search failed: ${String(s.reason)}`);
|
|
281
|
+
const failedIndex = traceSearches.length;
|
|
282
|
+
const search = searchPlan.searches[failedIndex - searchPlan.skipped.length];
|
|
283
|
+
traceSearches.push({
|
|
284
|
+
resourceType: search?.resourceType ?? "user",
|
|
285
|
+
targetUriInput: search?.targetUri,
|
|
286
|
+
limit: candidateLimit,
|
|
287
|
+
scoreThreshold: 0,
|
|
288
|
+
durationMs: 0,
|
|
289
|
+
total: 0,
|
|
290
|
+
results: [],
|
|
291
|
+
error: String(s.reason),
|
|
292
|
+
});
|
|
142
293
|
}
|
|
143
294
|
}
|
|
144
295
|
const uniqueMemories = allMemories.filter((memory, index, self) => index === self.findIndex((m) => m.uri === memory.uri));
|
|
145
296
|
const leafOnly = uniqueMemories.filter((m) => !m.level || m.level === 2);
|
|
146
297
|
const processed = postProcessMemories(leafOnly, {
|
|
147
298
|
limit: candidateLimit,
|
|
148
|
-
scoreThreshold
|
|
299
|
+
scoreThreshold,
|
|
149
300
|
});
|
|
150
|
-
const memories = pickMemoriesForInjection(processed,
|
|
301
|
+
const memories = pickMemoriesForInjection(processed, recallLimit, queryText, scoreThreshold, queryConfig ? {
|
|
302
|
+
weights: queryConfig.rankingWeights,
|
|
303
|
+
categoryWeights: queryConfig.categoryWeights,
|
|
304
|
+
resourceTypeWeights: queryConfig.resourceTypeWeights,
|
|
305
|
+
} : undefined);
|
|
306
|
+
const recordTrace = async (injectedMemories, injectedCount, estimatedTokens) => {
|
|
307
|
+
const entry = {
|
|
308
|
+
schemaVersion: "1.0",
|
|
309
|
+
traceId: newTraceId(),
|
|
310
|
+
ts: Date.now(),
|
|
311
|
+
sessionId: params.sessionId,
|
|
312
|
+
sessionKey: params.sessionKey,
|
|
313
|
+
ovSessionId: params.ovSessionId,
|
|
314
|
+
agentId,
|
|
315
|
+
source: "auto_recall",
|
|
316
|
+
operationType: "semantic_find",
|
|
317
|
+
resourceTypes: searchPlan.resourceTypes,
|
|
318
|
+
trigger: {
|
|
319
|
+
rawUserTextPreview: params.rawUserTextPreview,
|
|
320
|
+
...boundTraceQuery(queryText, cfg.traceRecallQueryMaxChars),
|
|
321
|
+
derivedKeywords: [],
|
|
322
|
+
queryTruncated: params.queryTruncated || queryText.length > cfg.traceRecallQueryMaxChars,
|
|
323
|
+
},
|
|
324
|
+
searches: traceSearches,
|
|
325
|
+
selected: injectedMemories.map((memory) => ({
|
|
326
|
+
uri: memory.uri,
|
|
327
|
+
resourceType: memory.uri.startsWith("viking://resources") ? "resource" : undefined,
|
|
328
|
+
category: memory.category,
|
|
329
|
+
score: memory.score,
|
|
330
|
+
abstractPreview: preview(memory.abstract ?? memory.overview, cfg.traceRecallPreviewChars),
|
|
331
|
+
injected: true,
|
|
332
|
+
})),
|
|
333
|
+
stats: {
|
|
334
|
+
candidateCount: allMemories.length,
|
|
335
|
+
selectedCount: injectedMemories.length,
|
|
336
|
+
injectedCount,
|
|
337
|
+
estimatedTokens,
|
|
338
|
+
},
|
|
339
|
+
};
|
|
340
|
+
// Trace persistence is diagnostic best-effort; never put JSONL flush latency on the auto-recall critical path.
|
|
341
|
+
params.traceRecorder?.record(entry);
|
|
342
|
+
};
|
|
151
343
|
if (memories.length === 0) {
|
|
344
|
+
await recordTrace([], 0, 0);
|
|
152
345
|
return { memoryCount: 0, estimatedTokens: 0 };
|
|
153
346
|
}
|
|
154
347
|
const { lines: memoryLines, estimatedTokens } = await buildMemoryLinesWithBudget(memories, (uri) => client.read(uri, agentId), {
|
|
155
|
-
recallPreferAbstract
|
|
156
|
-
recallMaxInjectedChars:
|
|
348
|
+
recallPreferAbstract,
|
|
349
|
+
recallMaxInjectedChars: maxInjectedChars,
|
|
350
|
+
includeUri: true,
|
|
157
351
|
});
|
|
158
352
|
if (memoryLines.length === 0) {
|
|
159
|
-
verbose?.(`openviking: skipping auto-recall injection; no complete memories fit maxInjectedChars=${
|
|
353
|
+
verbose?.(`openviking: skipping auto-recall injection; no complete memories fit maxInjectedChars=${maxInjectedChars}`);
|
|
354
|
+
await recordTrace([], 0, 0);
|
|
160
355
|
return { memoryCount: 0, estimatedTokens: 0 };
|
|
161
356
|
}
|
|
162
357
|
const block = buildRecallContextBlock(memoryLines);
|
|
163
|
-
verbose?.(`openviking: injecting ${memoryLines.length} memories (${block.length} chars, ~${estimatedTokens} tokens, maxInjectedChars=${
|
|
358
|
+
verbose?.(`openviking: injecting ${memoryLines.length} memories (${block.length} chars, ~${estimatedTokens} tokens, maxInjectedChars=${maxInjectedChars})`);
|
|
164
359
|
verbose?.(`openviking: inject-detail ${toJsonLog({ count: memories.length, memories: summarizeInjectionMemories(memories) })}`);
|
|
360
|
+
await recordTrace(memories.slice(0, memoryLines.length), memoryLines.length, estimatedTokens);
|
|
165
361
|
return { block, memoryCount: memoryLines.length, estimatedTokens };
|
|
166
362
|
})(), AUTO_RECALL_TIMEOUT_MS, "openviking: auto-recall search timeout");
|
|
167
363
|
}
|