@openviking/openclaw-plugin 2026.6.2 → 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 +4 -4
- package/INSTALL-ZH.md +68 -8
- package/INSTALL.md +12 -8
- package/README.md +45 -35
- package/adapters/http-transport.ts +3 -0
- package/adapters/resource-packager.ts +141 -0
- package/auto-recall.ts +258 -27
- package/client.ts +201 -177
- package/commands/setup.ts +200 -33
- package/config/feature-gates.json +10 -0
- package/config.ts +428 -85
- package/context-engine.ts +71 -1402
- package/dist/adapters/http-transport.js +1 -0
- package/dist/adapters/resource-packager.js +114 -0
- package/dist/auto-recall.js +201 -25
- package/dist/client.js +125 -161
- package/dist/commands/setup.js +170 -32
- package/dist/config.js +285 -64
- package/dist/context-engine.js +62 -1173
- package/dist/index.js +209 -1489
- 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 +1 -1
- 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/index.ts +271 -1873
- package/install-manifest.json +10 -1
- package/memory-ranking.ts +46 -10
- package/openclaw.plugin.json +198 -36
- package/package.json +14 -4
- 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 +1 -1
- 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/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 +0 -2
- package/skills/install-openviking-memory/SKILL.md +123 -50
- package/skills/openviking-context-database/SKILL.md +286 -0
- package/text-utils.ts +15 -34
|
@@ -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,10 +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";
|
|
4
5
|
import { estimateTextTokens } from "./token-estimator.js";
|
|
5
6
|
const AUTO_RECALL_TIMEOUT_MS = 5_000;
|
|
6
7
|
const RECALL_QUERY_MAX_CHARS = 4_000;
|
|
7
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;
|
|
8
16
|
export function prepareRecallQuery(rawText) {
|
|
9
17
|
const sanitized = sanitizeUserTextForCapture(rawText).trim();
|
|
10
18
|
const originalChars = sanitized.length;
|
|
@@ -119,8 +127,82 @@ export function buildRecallContextBlock(memoryLines) {
|
|
|
119
127
|
"</relevant-memories>",
|
|
120
128
|
].join("\n");
|
|
121
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
|
+
}
|
|
122
203
|
export async function buildAutoRecallContext(params) {
|
|
123
204
|
const { cfg, client, agentId, queryText, logger, verbose } = params;
|
|
205
|
+
const queryConfig = params.queryConfig;
|
|
124
206
|
if (!cfg.autoRecall || queryText.length < 5) {
|
|
125
207
|
return { memoryCount: 0, estimatedTokens: 0 };
|
|
126
208
|
}
|
|
@@ -130,58 +212,152 @@ export async function buildAutoRecallContext(params) {
|
|
|
130
212
|
return { memoryCount: 0, estimatedTokens: 0 };
|
|
131
213
|
}
|
|
132
214
|
return withTimeout((async () => {
|
|
133
|
-
const candidateLimit = Math.max(cfg.recallLimit * 4, 20);
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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,
|
|
149
237
|
limit: candidateLimit,
|
|
150
238
|
scoreThreshold: 0,
|
|
151
|
-
|
|
152
|
-
|
|
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
|
+
});
|
|
153
249
|
const autoRecallSettled = await Promise.allSettled(autoRecallPromises);
|
|
154
250
|
const allMemories = [];
|
|
155
251
|
for (const s of autoRecallSettled) {
|
|
156
252
|
if (s.status === "fulfilled") {
|
|
157
|
-
|
|
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
|
+
});
|
|
158
278
|
}
|
|
159
279
|
else {
|
|
160
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
|
+
});
|
|
161
293
|
}
|
|
162
294
|
}
|
|
163
295
|
const uniqueMemories = allMemories.filter((memory, index, self) => index === self.findIndex((m) => m.uri === memory.uri));
|
|
164
296
|
const leafOnly = uniqueMemories.filter((m) => !m.level || m.level === 2);
|
|
165
297
|
const processed = postProcessMemories(leafOnly, {
|
|
166
298
|
limit: candidateLimit,
|
|
167
|
-
scoreThreshold
|
|
299
|
+
scoreThreshold,
|
|
168
300
|
});
|
|
169
|
-
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
|
+
};
|
|
170
343
|
if (memories.length === 0) {
|
|
344
|
+
await recordTrace([], 0, 0);
|
|
171
345
|
return { memoryCount: 0, estimatedTokens: 0 };
|
|
172
346
|
}
|
|
173
347
|
const { lines: memoryLines, estimatedTokens } = await buildMemoryLinesWithBudget(memories, (uri) => client.read(uri, agentId), {
|
|
174
|
-
recallPreferAbstract
|
|
175
|
-
recallMaxInjectedChars:
|
|
348
|
+
recallPreferAbstract,
|
|
349
|
+
recallMaxInjectedChars: maxInjectedChars,
|
|
176
350
|
includeUri: true,
|
|
177
351
|
});
|
|
178
352
|
if (memoryLines.length === 0) {
|
|
179
|
-
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);
|
|
180
355
|
return { memoryCount: 0, estimatedTokens: 0 };
|
|
181
356
|
}
|
|
182
357
|
const block = buildRecallContextBlock(memoryLines);
|
|
183
|
-
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})`);
|
|
184
359
|
verbose?.(`openviking: inject-detail ${toJsonLog({ count: memories.length, memories: summarizeInjectionMemories(memories) })}`);
|
|
360
|
+
await recordTrace(memories.slice(0, memoryLines.length), memoryLines.length, estimatedTokens);
|
|
185
361
|
return { block, memoryCount: memoryLines.length, estimatedTokens };
|
|
186
362
|
})(), AUTO_RECALL_TIMEOUT_MS, "openviking: auto-recall search timeout");
|
|
187
363
|
}
|