@openviking/openclaw-plugin 2026.9.1 → 2026.9.2
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/README.md +3 -10
- package/auto-recall.ts +126 -135
- package/client.ts +95 -0
- package/config.ts +8 -6
- package/dist/auto-recall.js +113 -114
- package/dist/client.js +54 -0
- package/dist/config.js +6 -4
- package/dist/shared/recall-compress-core.mjs +188 -0
- package/dist/shared/recall-core.mjs +585 -0
- package/install-manifest.json +1 -0
- package/openclaw.plugin.json +2 -2
- package/package.json +2 -1
- package/shared/recall-compress-core.mjs +195 -0
- package/shared/recall-core.d.mts +39 -0
- package/shared/recall-core.mjs +613 -0
package/README.md
CHANGED
|
@@ -164,16 +164,9 @@ During recall, the plugin:
|
|
|
164
164
|
1. Extracts query text from the latest user message.
|
|
165
165
|
2. Resolves the agent routing for the current `sessionId/sessionKey`.
|
|
166
166
|
3. Runs a quick availability precheck so model requests do not stall when OpenViking is unavailable.
|
|
167
|
-
4.
|
|
168
|
-
5.
|
|
169
|
-
6. Prepends the
|
|
170
|
-
|
|
171
|
-
The reranking logic is not pure vector-score sorting. The current implementation also considers:
|
|
172
|
-
|
|
173
|
-
- whether a result is a leaf memory with `level == 2`
|
|
174
|
-
- whether it looks like a preference memory
|
|
175
|
-
- whether it looks like an event memory
|
|
176
|
-
- lexical overlap with the current query
|
|
167
|
+
4. Sends one session-aware context search for the configured `recallTargetTypes` (`user,agent` by default; optionally `resource`; use `ov_archive_search` and `ov_archive_expand` to inspect raw session history).
|
|
168
|
+
5. Lets OpenViking expand the query from session history, filter and rank candidates, apply cross-turn deduplication, select detail tiers, and assemble the result under the injection budget.
|
|
169
|
+
6. Prepends the server-rendered context inside `<relevant-memories>` to the current user message; it does not append a standalone synthetic user message.
|
|
177
170
|
|
|
178
171
|
## Session Lifecycle
|
|
179
172
|
|
package/auto-recall.ts
CHANGED
|
@@ -1,12 +1,7 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { FindResultItem, OpenVikingClient, SearchContextEntry } from "./client.js";
|
|
2
2
|
import type { MemoryOpenVikingConfig } from "./config.js";
|
|
3
3
|
import type { EffectiveQueryConfig } from "./query-config.js";
|
|
4
|
-
import {
|
|
5
|
-
pickMemoriesForInjection,
|
|
6
|
-
postProcessMemories,
|
|
7
|
-
summarizeInjectionMemories,
|
|
8
|
-
toJsonLog,
|
|
9
|
-
} from "./memory-ranking.js";
|
|
4
|
+
import { toJsonLog } from "./memory-ranking.js";
|
|
10
5
|
import { quickRecallPrecheck, withTimeout } from "./process-manager.js";
|
|
11
6
|
import {
|
|
12
7
|
resolveRecallSearchPlan,
|
|
@@ -20,6 +15,8 @@ import { sanitizeUserTextForCapture } from "./text-utils.js";
|
|
|
20
15
|
import { estimateTextTokens } from "./token-estimator.js";
|
|
21
16
|
|
|
22
17
|
const RECALL_QUERY_MAX_CHARS = 4_000;
|
|
18
|
+
const AUTO_RECALL_DEDUP_TURNS = 5;
|
|
19
|
+
const LEGACY_CHARS_PER_TOKEN = 4;
|
|
23
20
|
export const AUTO_RECALL_SOURCE_MARKER = "Source: openviking-auto-recall";
|
|
24
21
|
|
|
25
22
|
type Logger = {
|
|
@@ -231,16 +228,26 @@ function preview(value: string | undefined, maxChars: number): string | undefine
|
|
|
231
228
|
return trimmed.length > maxChars ? trimmed.slice(0, maxChars) : trimmed;
|
|
232
229
|
}
|
|
233
230
|
|
|
234
|
-
function
|
|
235
|
-
|
|
236
|
-
|
|
231
|
+
function traceResourceType(entry: SearchContextEntry): RecallResourceType {
|
|
232
|
+
if (entry.category === "resources" || entry.uri.startsWith("viking://resources")) {
|
|
233
|
+
return "resource";
|
|
234
|
+
}
|
|
235
|
+
if (entry.origin === "actor_peer") {
|
|
236
|
+
return "agent";
|
|
237
|
+
}
|
|
238
|
+
return "user";
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function toTraceResult(entry: SearchContextEntry): RecallTraceResult {
|
|
242
|
+
const resourceType = traceResourceType(entry);
|
|
243
|
+
return {
|
|
244
|
+
uri: entry.uri,
|
|
237
245
|
resourceType,
|
|
238
|
-
category:
|
|
239
|
-
score:
|
|
240
|
-
|
|
241
|
-
abstractPreview: preview(item.abstract ?? item.overview, 240),
|
|
246
|
+
category: entry.category,
|
|
247
|
+
score: entry.score,
|
|
248
|
+
abstractPreview: preview(entry.text, 240),
|
|
242
249
|
resultType: resourceType === "resource" ? "resource" : "memory",
|
|
243
|
-
}
|
|
250
|
+
};
|
|
244
251
|
}
|
|
245
252
|
|
|
246
253
|
function boundTraceQuery(query: string, maxChars: number): { query: string; queryTruncated?: boolean } {
|
|
@@ -340,7 +347,6 @@ export async function buildAutoRecallContext(params: {
|
|
|
340
347
|
|
|
341
348
|
return withTimeout(
|
|
342
349
|
(async () => {
|
|
343
|
-
const candidateLimit = queryConfig?.candidateLimit ?? Math.max(cfg.recallLimit * 4, 20);
|
|
344
350
|
const scoreThreshold = queryConfig?.scoreThreshold ?? cfg.recallScoreThreshold;
|
|
345
351
|
const recallLimit = queryConfig?.recallLimit ?? cfg.recallLimit;
|
|
346
352
|
const maxInjectedChars = queryConfig?.maxInjectedChars ?? cfg.recallMaxInjectedChars;
|
|
@@ -349,98 +355,88 @@ export async function buildAutoRecallContext(params: {
|
|
|
349
355
|
ovSessionId: params.ovSessionId,
|
|
350
356
|
agentId,
|
|
351
357
|
});
|
|
352
|
-
const
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
contextType: search.contextType,
|
|
358
|
+
const contextTypes = [...new Set(searchPlan.searches.map((search) => search.contextType))];
|
|
359
|
+
const maxTokens = Math.min(
|
|
360
|
+
32_000,
|
|
361
|
+
Math.max(64, Math.round(maxInjectedChars / LEGACY_CHARS_PER_TOKEN)),
|
|
362
|
+
);
|
|
363
|
+
const startedAt = Date.now();
|
|
364
|
+
let contextResult: Awaited<ReturnType<OpenVikingClient["searchContext"]>> | undefined;
|
|
365
|
+
let requestError: unknown;
|
|
366
|
+
|
|
367
|
+
try {
|
|
368
|
+
contextResult = await client.searchContext(queryText, {
|
|
369
|
+
sessionId: params.ovSessionId,
|
|
370
|
+
limit: recallLimit,
|
|
371
|
+
scoreThreshold,
|
|
372
|
+
contextType: contextTypes.length === 1 ? contextTypes[0] : contextTypes,
|
|
373
|
+
queryExpansion: "auto",
|
|
374
|
+
maxTokens,
|
|
375
|
+
detail: recallPreferAbstract ? "abstract" : undefined,
|
|
376
|
+
dedupTurns: params.ovSessionId ? AUTO_RECALL_DEDUP_TURNS : undefined,
|
|
377
|
+
peerScope: "actor",
|
|
373
378
|
actorPeerId,
|
|
379
|
+
requestTimeoutMs: cfg.autoRecallTimeoutMs,
|
|
374
380
|
});
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
targetUri: search.targetUri,
|
|
378
|
-
result,
|
|
379
|
-
durationMs: Date.now() - start,
|
|
380
|
-
};
|
|
381
|
-
});
|
|
382
|
-
const autoRecallSettled = await Promise.allSettled(autoRecallPromises);
|
|
383
|
-
|
|
384
|
-
const allMemories: FindResultItem[] = [];
|
|
385
|
-
for (const s of autoRecallSettled) {
|
|
386
|
-
if (s.status === "fulfilled") {
|
|
387
|
-
const result = s.value.result ?? {};
|
|
388
|
-
const memories = result.memories ?? [];
|
|
389
|
-
const resources = result.resources ?? [];
|
|
390
|
-
allMemories.push(...memories, ...resources);
|
|
391
|
-
traceSearches.push({
|
|
392
|
-
resourceType: s.value.resourceType,
|
|
393
|
-
targetUriInput: s.value.targetUri,
|
|
394
|
-
limit: candidateLimit,
|
|
395
|
-
scoreThreshold: 0,
|
|
396
|
-
durationMs: s.value.durationMs,
|
|
397
|
-
total: result.total ?? memories.length + resources.length + (result.skills?.length ?? 0),
|
|
398
|
-
results: [
|
|
399
|
-
...toTraceResults(memories, s.value.resourceType),
|
|
400
|
-
...toTraceResults(resources, "resource"),
|
|
401
|
-
...(result.skills ?? []).map((item): RecallTraceResult => ({
|
|
402
|
-
uri: item.uri,
|
|
403
|
-
resourceType: s.value.resourceType,
|
|
404
|
-
category: item.category,
|
|
405
|
-
score: item.score,
|
|
406
|
-
level: item.level,
|
|
407
|
-
abstractPreview: preview(item.abstract ?? item.overview, 240),
|
|
408
|
-
resultType: "skill",
|
|
409
|
-
})),
|
|
410
|
-
].slice(0, cfg.traceRecallMaxResultsPerSearch),
|
|
411
|
-
});
|
|
412
|
-
} else {
|
|
413
|
-
logger.warn?.(`openviking: auto-recall search failed: ${String(s.reason)}`);
|
|
414
|
-
const failedIndex = traceSearches.length;
|
|
415
|
-
const search = searchPlan.searches[failedIndex - searchPlan.skipped.length];
|
|
416
|
-
traceSearches.push({
|
|
417
|
-
resourceType: search?.resourceType ?? "user",
|
|
418
|
-
targetUriInput: search?.targetUri,
|
|
419
|
-
limit: candidateLimit,
|
|
420
|
-
scoreThreshold: 0,
|
|
421
|
-
durationMs: 0,
|
|
422
|
-
total: 0,
|
|
423
|
-
results: [],
|
|
424
|
-
error: String(s.reason),
|
|
425
|
-
});
|
|
426
|
-
}
|
|
381
|
+
} catch (error) {
|
|
382
|
+
requestError = error;
|
|
427
383
|
}
|
|
428
384
|
|
|
429
|
-
const
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
const
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
385
|
+
const durationMs = Date.now() - startedAt;
|
|
386
|
+
const entries = (contextResult?.entries ?? []).filter((entry) => Boolean(entry.uri));
|
|
387
|
+
const rawRetrievalErrors = contextResult?.stats?.retrieval_errors;
|
|
388
|
+
const retrievalErrors = Array.isArray(rawRetrievalErrors)
|
|
389
|
+
? rawRetrievalErrors.map((error) => String(error))
|
|
390
|
+
: [];
|
|
391
|
+
const searchError = requestError
|
|
392
|
+
? String(requestError)
|
|
393
|
+
: retrievalErrors.length > 0
|
|
394
|
+
? retrievalErrors.join("; ")
|
|
395
|
+
: undefined;
|
|
396
|
+
|
|
397
|
+
if (searchError) {
|
|
398
|
+
logger.warn?.(`openviking: auto-recall context search failed: ${searchError}`);
|
|
399
|
+
}
|
|
442
400
|
|
|
443
|
-
const
|
|
401
|
+
const traceSearches: RecallTraceEntry["searches"] = [
|
|
402
|
+
...searchPlan.skipped.map((skipped) => ({
|
|
403
|
+
resourceType: skipped.resourceType,
|
|
404
|
+
limit: recallLimit,
|
|
405
|
+
scoreThreshold,
|
|
406
|
+
durationMs: 0,
|
|
407
|
+
total: 0,
|
|
408
|
+
results: [],
|
|
409
|
+
error: skipped.reason,
|
|
410
|
+
})),
|
|
411
|
+
...searchPlan.searches.map((search) => {
|
|
412
|
+
const matchingEntries = entries.filter((entry) =>
|
|
413
|
+
search.contextType === "resource"
|
|
414
|
+
? traceResourceType(entry) === "resource"
|
|
415
|
+
: traceResourceType(entry) !== "resource"
|
|
416
|
+
);
|
|
417
|
+
return {
|
|
418
|
+
resourceType: search.resourceType,
|
|
419
|
+
contextType: search.contextType,
|
|
420
|
+
limit: recallLimit,
|
|
421
|
+
scoreThreshold,
|
|
422
|
+
durationMs,
|
|
423
|
+
total: matchingEntries.length,
|
|
424
|
+
results: matchingEntries
|
|
425
|
+
.map(toTraceResult)
|
|
426
|
+
.slice(0, cfg.traceRecallMaxResultsPerSearch),
|
|
427
|
+
error: searchError,
|
|
428
|
+
};
|
|
429
|
+
}),
|
|
430
|
+
];
|
|
431
|
+
|
|
432
|
+
const candidateCount = typeof contextResult?.stats?.candidates === "number"
|
|
433
|
+
? contextResult.stats.candidates
|
|
434
|
+
: entries.length;
|
|
435
|
+
const recordTrace = async (
|
|
436
|
+
injectedEntries: SearchContextEntry[],
|
|
437
|
+
injectedCount: number,
|
|
438
|
+
estimatedTokens?: number,
|
|
439
|
+
) => {
|
|
444
440
|
const entry: RecallTraceEntry = {
|
|
445
441
|
schemaVersion: "1.0",
|
|
446
442
|
traceId: newTraceId(),
|
|
@@ -459,17 +455,17 @@ export async function buildAutoRecallContext(params: {
|
|
|
459
455
|
queryTruncated: params.queryTruncated || queryText.length > cfg.traceRecallQueryMaxChars,
|
|
460
456
|
},
|
|
461
457
|
searches: traceSearches,
|
|
462
|
-
selected:
|
|
463
|
-
uri:
|
|
464
|
-
resourceType:
|
|
465
|
-
category:
|
|
466
|
-
score:
|
|
467
|
-
abstractPreview: preview(
|
|
458
|
+
selected: injectedEntries.map((contextEntry) => ({
|
|
459
|
+
uri: contextEntry.uri,
|
|
460
|
+
resourceType: traceResourceType(contextEntry),
|
|
461
|
+
category: contextEntry.category,
|
|
462
|
+
score: contextEntry.score,
|
|
463
|
+
abstractPreview: preview(contextEntry.text, cfg.traceRecallPreviewChars),
|
|
468
464
|
injected: true,
|
|
469
465
|
})),
|
|
470
466
|
stats: {
|
|
471
|
-
candidateCount
|
|
472
|
-
selectedCount:
|
|
467
|
+
candidateCount,
|
|
468
|
+
selectedCount: injectedEntries.length,
|
|
473
469
|
injectedCount,
|
|
474
470
|
estimatedTokens,
|
|
475
471
|
},
|
|
@@ -478,39 +474,34 @@ export async function buildAutoRecallContext(params: {
|
|
|
478
474
|
params.traceRecorder?.record(entry);
|
|
479
475
|
};
|
|
480
476
|
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
return { memoryCount: 0, estimatedTokens: 0 };
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
const { lines: memoryLines, estimatedTokens } = await buildMemoryLinesWithBudget(
|
|
487
|
-
memories,
|
|
488
|
-
(uri) => client.read(uri, actorPeerId),
|
|
489
|
-
{
|
|
490
|
-
recallPreferAbstract,
|
|
491
|
-
recallMaxInjectedChars: maxInjectedChars,
|
|
492
|
-
includeUri: true,
|
|
493
|
-
},
|
|
494
|
-
);
|
|
495
|
-
|
|
496
|
-
if (memoryLines.length === 0) {
|
|
497
|
-
verbose?.(
|
|
498
|
-
`openviking: skipping auto-recall injection; no complete memories fit maxInjectedChars=${maxInjectedChars}`,
|
|
499
|
-
);
|
|
477
|
+
const rendered = contextResult?.rendered?.trim() ?? "";
|
|
478
|
+
if (requestError || entries.length === 0 || !rendered) {
|
|
500
479
|
await recordTrace([], 0, 0);
|
|
501
480
|
return { memoryCount: 0, estimatedTokens: 0 };
|
|
502
481
|
}
|
|
503
482
|
|
|
504
|
-
const
|
|
483
|
+
const usedTokens = contextResult?.stats?.used_tokens;
|
|
484
|
+
const estimatedTokens = typeof usedTokens === "number" && Number.isFinite(usedTokens)
|
|
485
|
+
? Math.max(0, Math.ceil(usedTokens))
|
|
486
|
+
: estimateTokenCount(rendered);
|
|
487
|
+
const block = buildRecallContextBlock([rendered]);
|
|
505
488
|
verbose?.(
|
|
506
|
-
`openviking: injecting ${
|
|
489
|
+
`openviking: injecting ${entries.length} memories (${block.length} chars, ~${estimatedTokens} tokens, serverMaxTokens=${maxTokens})`,
|
|
507
490
|
);
|
|
508
491
|
verbose?.(
|
|
509
|
-
`openviking: inject-detail ${toJsonLog({
|
|
492
|
+
`openviking: inject-detail ${toJsonLog({
|
|
493
|
+
count: entries.length,
|
|
494
|
+
entries: entries.map((contextEntry) => ({
|
|
495
|
+
uri: contextEntry.uri,
|
|
496
|
+
category: contextEntry.category,
|
|
497
|
+
score: contextEntry.score,
|
|
498
|
+
detail: contextEntry.detail,
|
|
499
|
+
})),
|
|
500
|
+
})}`,
|
|
510
501
|
);
|
|
511
502
|
|
|
512
|
-
await recordTrace(
|
|
513
|
-
return { block, memoryCount:
|
|
503
|
+
await recordTrace(entries, entries.length, estimatedTokens);
|
|
504
|
+
return { block, memoryCount: entries.length, estimatedTokens };
|
|
514
505
|
})(),
|
|
515
506
|
cfg.autoRecallTimeoutMs,
|
|
516
507
|
"openviking: auto-recall search timeout",
|
package/client.ts
CHANGED
|
@@ -12,6 +12,11 @@ import {
|
|
|
12
12
|
defaultResourcePackager,
|
|
13
13
|
type ResourcePackager,
|
|
14
14
|
} from "./adapters/resource-packager.js";
|
|
15
|
+
import {
|
|
16
|
+
buildContextSearchBody,
|
|
17
|
+
contextRequestTimeoutMs,
|
|
18
|
+
normalizeContextEntry,
|
|
19
|
+
} from "./shared/recall-core.mjs";
|
|
15
20
|
|
|
16
21
|
export type FindResultItem = {
|
|
17
22
|
uri: string;
|
|
@@ -30,6 +35,36 @@ export type FindResult = {
|
|
|
30
35
|
total?: number;
|
|
31
36
|
};
|
|
32
37
|
|
|
38
|
+
export type SearchContextEntry = {
|
|
39
|
+
uri: string;
|
|
40
|
+
category?: string;
|
|
41
|
+
score?: number;
|
|
42
|
+
detail?: string;
|
|
43
|
+
text?: string;
|
|
44
|
+
origin?: string;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export type SearchContextResult = {
|
|
48
|
+
entries?: SearchContextEntry[];
|
|
49
|
+
rendered?: string;
|
|
50
|
+
digest?: string;
|
|
51
|
+
stats?: Record<string, unknown>;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export type SearchContextOptions = {
|
|
55
|
+
sessionId?: string;
|
|
56
|
+
limit?: number;
|
|
57
|
+
scoreThreshold?: number;
|
|
58
|
+
contextType?: string | string[];
|
|
59
|
+
queryExpansion?: "off" | "auto";
|
|
60
|
+
maxTokens?: number;
|
|
61
|
+
detail?: "abstract" | "overview" | "full";
|
|
62
|
+
dedupTurns?: number;
|
|
63
|
+
peerScope?: "actor" | "all";
|
|
64
|
+
actorPeerId?: string;
|
|
65
|
+
requestTimeoutMs?: number;
|
|
66
|
+
};
|
|
67
|
+
|
|
33
68
|
export type FsListEntry = string | Record<string, unknown>;
|
|
34
69
|
|
|
35
70
|
export type FsListResult = FsListEntry[];
|
|
@@ -488,6 +523,66 @@ export class OpenVikingClient {
|
|
|
488
523
|
}, undefined, actorPeerId);
|
|
489
524
|
}
|
|
490
525
|
|
|
526
|
+
async searchContext(
|
|
527
|
+
query: string,
|
|
528
|
+
options: SearchContextOptions = {},
|
|
529
|
+
): Promise<SearchContextResult> {
|
|
530
|
+
const contractConfig = {
|
|
531
|
+
recallLimit: options.limit,
|
|
532
|
+
recallLimitConfigured: options.limit !== undefined,
|
|
533
|
+
recallMaxTokens: options.maxTokens,
|
|
534
|
+
recallMaxTokensConfigured: options.maxTokens !== undefined,
|
|
535
|
+
scoreThreshold: options.scoreThreshold,
|
|
536
|
+
recallQueryExpansion: options.queryExpansion,
|
|
537
|
+
recallQueryExpansionConfigured: options.queryExpansion !== undefined,
|
|
538
|
+
recallDedupTurns: options.dedupTurns,
|
|
539
|
+
recallPeerScope: options.peerScope,
|
|
540
|
+
recallContextTimeoutMs: options.requestTimeoutMs,
|
|
541
|
+
timeoutMs: this.timeoutMs,
|
|
542
|
+
};
|
|
543
|
+
const body = {
|
|
544
|
+
...buildContextSearchBody(contractConfig, { sessionId: options.sessionId }),
|
|
545
|
+
query,
|
|
546
|
+
...(options.contextType !== undefined ? { context_type: options.contextType } : {}),
|
|
547
|
+
...(options.detail !== undefined ? { detail: options.detail } : {}),
|
|
548
|
+
};
|
|
549
|
+
const requestTimeoutMs = contextRequestTimeoutMs(contractConfig, body);
|
|
550
|
+
const actorPeerId = this.resolveActorPeerHeader(options.actorPeerId);
|
|
551
|
+
const tenantHeaders = this.resolveTenantHeaders();
|
|
552
|
+
this.routingDebugLog?.(
|
|
553
|
+
`openviking: context search POST ${this.baseUrl}/api/v1/search/search ` +
|
|
554
|
+
JSON.stringify({
|
|
555
|
+
X_OpenViking_Account: tenantHeaders.accountId ?? null,
|
|
556
|
+
X_OpenViking_User: tenantHeaders.userId ?? null,
|
|
557
|
+
X_OpenViking_Actor_Peer: actorPeerId ?? null,
|
|
558
|
+
session_id: body.session_id ?? null,
|
|
559
|
+
query:
|
|
560
|
+
query.length > 4000
|
|
561
|
+
? `${query.slice(0, 4000)}…(+${query.length - 4000} more chars)`
|
|
562
|
+
: query,
|
|
563
|
+
purpose: body.purpose,
|
|
564
|
+
quotas: body.quotas ?? null,
|
|
565
|
+
score_threshold: body.score_threshold,
|
|
566
|
+
context_type: body.context_type ?? null,
|
|
567
|
+
query_expansion: body.query_expansion ?? null,
|
|
568
|
+
max_tokens: body.max_tokens ?? null,
|
|
569
|
+
detail: body.detail ?? null,
|
|
570
|
+
dedup_turns: body.dedup_turns ?? 0,
|
|
571
|
+
peer_scope: body.peer_scope ?? null,
|
|
572
|
+
}),
|
|
573
|
+
);
|
|
574
|
+
const result = await this.request<SearchContextResult>("/api/v1/search/search", {
|
|
575
|
+
method: "POST",
|
|
576
|
+
body: JSON.stringify(body),
|
|
577
|
+
}, requestTimeoutMs, actorPeerId);
|
|
578
|
+
return {
|
|
579
|
+
...result,
|
|
580
|
+
entries: Array.isArray(result.entries)
|
|
581
|
+
? result.entries.map((entry) => ({ ...entry, ...normalizeContextEntry(entry) }))
|
|
582
|
+
: result.entries,
|
|
583
|
+
};
|
|
584
|
+
}
|
|
585
|
+
|
|
491
586
|
async read(uri: string, actorPeerId?: string): Promise<string> {
|
|
492
587
|
return this.request<string>(
|
|
493
588
|
`/api/v1/content/read?uri=${encodeURIComponent(uri)}`,
|
package/config.ts
CHANGED
|
@@ -45,13 +45,13 @@ export type MemoryOpenVikingConfig = {
|
|
|
45
45
|
captureMode?: "semantic" | "keyword";
|
|
46
46
|
captureMaxLength?: number;
|
|
47
47
|
autoRecall?: boolean;
|
|
48
|
-
/** Outer time budget for the whole auto-recall flow
|
|
48
|
+
/** Outer time budget for the whole server-assembled auto-recall flow. */
|
|
49
49
|
autoRecallTimeoutMs?: number;
|
|
50
50
|
/** Include resources in auto-recall and default memory_recall search. Default false. */
|
|
51
51
|
recallResources?: boolean;
|
|
52
52
|
recallLimit?: number;
|
|
53
53
|
recallScoreThreshold?: number;
|
|
54
|
-
/**
|
|
54
|
+
/** Legacy character budget, converted to the server context token budget. */
|
|
55
55
|
recallMaxInjectedChars?: number;
|
|
56
56
|
/** @deprecated Auto-recall no longer truncates individual memories. */
|
|
57
57
|
recallMaxContentChars?: number;
|
|
@@ -140,7 +140,9 @@ const DEFAULT_TARGET_URI = "viking://~/memories";
|
|
|
140
140
|
const DEFAULT_TIMEOUT_MS = 15000;
|
|
141
141
|
const DEFAULT_CAPTURE_MODE = "semantic";
|
|
142
142
|
const DEFAULT_CAPTURE_MAX_LENGTH = 24000;
|
|
143
|
-
|
|
143
|
+
// Session-aware context search may spend up to 5 seconds on query expansion
|
|
144
|
+
// before returning its server-side fallback, so leave headroom for retrieval.
|
|
145
|
+
const DEFAULT_AUTO_RECALL_TIMEOUT_MS = 15000;
|
|
144
146
|
const DEFAULT_RECALL_LIMIT = 6;
|
|
145
147
|
const DEFAULT_RECALL_SCORE_THRESHOLD = 0.15;
|
|
146
148
|
const DEFAULT_RECALL_MAX_CONTENT_CHARS = 5000;
|
|
@@ -843,7 +845,7 @@ export const memoryOpenVikingConfigSchema = {
|
|
|
843
845
|
label: "Auto-Recall Timeout (ms)",
|
|
844
846
|
placeholder: String(DEFAULT_AUTO_RECALL_TIMEOUT_MS),
|
|
845
847
|
advanced: true,
|
|
846
|
-
help: "Outer time budget for the
|
|
848
|
+
help: "Outer time budget for the server-assembled context search, including session-aware query expansion.",
|
|
847
849
|
},
|
|
848
850
|
recallResources: {
|
|
849
851
|
label: "Recall Resources",
|
|
@@ -870,7 +872,7 @@ export const memoryOpenVikingConfigSchema = {
|
|
|
870
872
|
label: "Recall Max Injected Chars",
|
|
871
873
|
placeholder: String(DEFAULT_RECALL_MAX_INJECTED_CHARS),
|
|
872
874
|
advanced: true,
|
|
873
|
-
help: "
|
|
875
|
+
help: "Legacy auto-recall character budget. It is converted to the server context token budget at four characters per token.",
|
|
874
876
|
},
|
|
875
877
|
recallMaxContentChars: {
|
|
876
878
|
label: "Deprecated Recall Max Content Chars",
|
|
@@ -881,7 +883,7 @@ export const memoryOpenVikingConfigSchema = {
|
|
|
881
883
|
recallPreferAbstract: {
|
|
882
884
|
label: "Recall Prefer Abstract",
|
|
883
885
|
advanced: true,
|
|
884
|
-
help: "
|
|
886
|
+
help: "Pin server-assembled auto-recall entries to abstract detail. When disabled, the server chooses each category's default detail tier.",
|
|
885
887
|
},
|
|
886
888
|
recallTokenBudget: {
|
|
887
889
|
label: "Deprecated Recall Token Budget",
|