@openviking/openclaw-plugin 2026.6.2 → 2026.6.17-dev.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.
Files changed (108) hide show
  1. package/INSTALL-AGENT.md +4 -4
  2. package/INSTALL-ZH.md +68 -8
  3. package/INSTALL.md +12 -8
  4. package/README.md +45 -35
  5. package/adapters/http-transport.ts +3 -0
  6. package/adapters/resource-packager.ts +141 -0
  7. package/auto-recall.ts +258 -27
  8. package/client.ts +201 -177
  9. package/commands/setup.ts +200 -33
  10. package/config/feature-gates.json +10 -0
  11. package/config.ts +428 -85
  12. package/context-engine.ts +71 -1402
  13. package/dist/adapters/http-transport.js +1 -0
  14. package/dist/adapters/resource-packager.js +114 -0
  15. package/dist/auto-recall.js +201 -25
  16. package/dist/client.js +125 -161
  17. package/dist/commands/setup.js +170 -32
  18. package/dist/config.js +285 -64
  19. package/dist/context-engine.js +62 -1173
  20. package/dist/index.js +209 -1489
  21. package/dist/memory-ranking.js +35 -12
  22. package/dist/plugin/command-registration.js +5 -0
  23. package/dist/plugin/openviking-archive-tools.js +210 -0
  24. package/dist/plugin/openviking-bypass-runtime.js +8 -0
  25. package/dist/plugin/openviking-client-runtime.js +28 -0
  26. package/dist/plugin/openviking-command-args.js +146 -0
  27. package/dist/plugin/openviking-command-definitions.js +206 -0
  28. package/dist/plugin/openviking-context-engine-ref.js +11 -0
  29. package/dist/plugin/openviking-context-engine-registration.js +23 -0
  30. package/dist/plugin/openviking-feature-gates.js +250 -0
  31. package/dist/plugin/openviking-import-runtime.js +50 -0
  32. package/dist/plugin/openviking-import-tools.js +86 -0
  33. package/dist/plugin/openviking-lifecycle-hooks.js +33 -0
  34. package/dist/plugin/openviking-memory-recall-tools.js +233 -0
  35. package/dist/plugin/openviking-memory-tools.js +199 -0
  36. package/dist/plugin/openviking-query-config-command.js +203 -0
  37. package/dist/plugin/openviking-query-runtime.js +367 -0
  38. package/dist/plugin/openviking-query-tools.js +91 -0
  39. package/dist/plugin/openviking-recall-trace-runtime.js +387 -0
  40. package/dist/plugin/openviking-recall-trace-tools.js +38 -0
  41. package/dist/plugin/openviking-runtime-state.js +26 -0
  42. package/dist/plugin/openviking-runtime-utils.js +62 -0
  43. package/dist/plugin/openviking-services.js +20 -0
  44. package/dist/plugin/openviking-session-routing-runtime.js +61 -0
  45. package/dist/plugin/openviking-tool-result-tools.js +268 -0
  46. package/dist/plugin/recall-trace-routes.js +77 -0
  47. package/dist/plugin/tool-registration.js +23 -0
  48. package/dist/process-manager.js +1 -1
  49. package/dist/query-config.js +336 -0
  50. package/dist/recall-trace.js +309 -0
  51. package/dist/registries/recall-resource-types.js +58 -0
  52. package/dist/routing/identity-routing.js +152 -0
  53. package/dist/routing/memory-uri.js +7 -0
  54. package/dist/services/context-lifecycle-service.js +847 -0
  55. package/dist/services/context-message-adapter.js +340 -0
  56. package/dist/session-transcript-repair.js +0 -1
  57. package/dist/text-utils.js +14 -31
  58. package/index.ts +271 -1873
  59. package/install-manifest.json +10 -1
  60. package/memory-ranking.ts +46 -10
  61. package/openclaw.plugin.json +198 -36
  62. package/package.json +14 -4
  63. package/plugin/command-registration.ts +34 -0
  64. package/plugin/openviking-archive-tools.ts +301 -0
  65. package/plugin/openviking-bypass-runtime.ts +26 -0
  66. package/plugin/openviking-client-runtime.ts +74 -0
  67. package/plugin/openviking-command-args.ts +187 -0
  68. package/plugin/openviking-command-definitions.ts +294 -0
  69. package/plugin/openviking-context-engine-ref.ts +16 -0
  70. package/plugin/openviking-context-engine-registration.ts +97 -0
  71. package/plugin/openviking-feature-gates.ts +335 -0
  72. package/plugin/openviking-import-runtime.ts +105 -0
  73. package/plugin/openviking-import-tools.ts +133 -0
  74. package/plugin/openviking-lifecycle-hooks.ts +65 -0
  75. package/plugin/openviking-memory-recall-tools.ts +368 -0
  76. package/plugin/openviking-memory-tools.ts +299 -0
  77. package/plugin/openviking-query-config-command.ts +230 -0
  78. package/plugin/openviking-query-formatters.ts +108 -0
  79. package/plugin/openviking-query-runtime.ts +476 -0
  80. package/plugin/openviking-query-tools.ts +161 -0
  81. package/plugin/openviking-recall-trace-runtime.ts +482 -0
  82. package/plugin/openviking-recall-trace-tools.ts +99 -0
  83. package/plugin/openviking-runtime-state.ts +52 -0
  84. package/plugin/openviking-runtime-utils.ts +67 -0
  85. package/plugin/openviking-services.ts +49 -0
  86. package/plugin/openviking-session-routing-runtime.ts +105 -0
  87. package/plugin/openviking-tool-result-tools.ts +397 -0
  88. package/plugin/recall-trace-routes.ts +133 -0
  89. package/plugin/tool-registration.ts +51 -0
  90. package/process-manager.ts +1 -1
  91. package/query-config.ts +400 -0
  92. package/recall-trace.ts +442 -0
  93. package/registries/openviking-tools.ts +84 -0
  94. package/registries/recall-resource-types.ts +74 -0
  95. package/routing/identity-routing.ts +210 -0
  96. package/routing/memory-uri.ts +8 -0
  97. package/services/context-lifecycle-service.ts +1209 -0
  98. package/services/context-message-adapter.ts +375 -0
  99. package/services/setup/config-writer.ts +125 -0
  100. package/services/setup/exit-utils.ts +5 -0
  101. package/services/setup/package-metadata.ts +56 -0
  102. package/services/setup/probe-service.ts +143 -0
  103. package/services/setup/setup-flow.ts +306 -0
  104. package/services/setup/version-compatibility.ts +41 -0
  105. package/session-transcript-repair.ts +0 -2
  106. package/skills/install-openviking-memory/SKILL.md +123 -50
  107. package/skills/openviking-context-database/SKILL.md +286 -0
  108. package/text-utils.ts +15 -34
package/auto-recall.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import type { FindResult, FindResultItem, OpenVikingClient } from "./client.js";
2
2
  import type { MemoryOpenVikingConfig } from "./config.js";
3
+ import type { EffectiveQueryConfig } from "./query-config.js";
3
4
  import {
4
5
  pickMemoriesForInjection,
5
6
  postProcessMemories,
@@ -7,6 +8,14 @@ import {
7
8
  toJsonLog,
8
9
  } from "./memory-ranking.js";
9
10
  import { quickRecallPrecheck, withTimeout } from "./process-manager.js";
11
+ import {
12
+ resolveRecallSearchPlan,
13
+ type RecallResourceType,
14
+ } from "./registries/recall-resource-types.js";
15
+ import {
16
+ type RecallTraceEntry,
17
+ type RecallTraceResult,
18
+ } from "./recall-trace.js";
10
19
  import { sanitizeUserTextForCapture } from "./text-utils.js";
11
20
  import { estimateTextTokens } from "./token-estimator.js";
12
21
 
@@ -19,6 +28,34 @@ type Logger = {
19
28
  warn?: (msg: string) => void;
20
29
  };
21
30
 
31
+ const WRITE_OR_EFFECT_RE =
32
+ /\b(write|edit|modify|delete|remove|migrate|deploy|release|publish|configure|patch)\b|写|改|修改|删除|迁移|部署|发布|配置|打补丁/i;
33
+ const EXECUTION_RE =
34
+ /\b(fix|debug|test|build|run|implement|refactor|integrate|repair|troubleshoot)\b|修复|调试|测试|构建|运行|实现|重构|对接|排查/i;
35
+ const FAILURE_RE =
36
+ /\b(error|exception|traceback|failed|failure|retry|exit code|test failed)\b|报错|异常|失败|重试|挂了|不通过/i;
37
+ const ENGINEERING_OBJECT_RE =
38
+ /(?:^|\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;
39
+ const EXPERIENCE_INTENT_RE =
40
+ /经验|踩坑|最佳实践|不要再|按之前|avoid|best practice|lesson|pitfall/i;
41
+ const QUESTION_ONLY_RE =
42
+ /^(?:什么是|是什么|区别|解释|讲讲|怎么看|为什么|如何理解|where is|what is|explain|difference between)\b|[??]$/i;
43
+ const CASUAL_RE = /闲聊|翻译|总结当前对话|天气|笑话|hello|hi\b|你好/i;
44
+
45
+ export type ExperienceRecallTrigger =
46
+ | "task_start"
47
+ | "skill_load"
48
+ | "subagent_start"
49
+ | "write_preflight"
50
+ | "cron_start";
51
+
52
+ export type ExperienceRecallDecision = {
53
+ recall: boolean;
54
+ trigger?: ExperienceRecallTrigger;
55
+ score: number;
56
+ reason: string;
57
+ };
58
+
22
59
  export type PreparedRecallQuery = {
23
60
  query: string;
24
61
  truncated: boolean;
@@ -185,15 +222,111 @@ export function buildRecallContextBlock(memoryLines: string[]): string {
185
222
  ].join("\n");
186
223
  }
187
224
 
225
+ function newTraceId(): string {
226
+ return `recall_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
227
+ }
228
+
229
+ function preview(value: string | undefined, maxChars: number): string | undefined {
230
+ const trimmed = value?.trim();
231
+ if (!trimmed) return undefined;
232
+ return trimmed.length > maxChars ? trimmed.slice(0, maxChars) : trimmed;
233
+ }
234
+
235
+ function toTraceResults(items: FindResultItem[], resourceType: RecallResourceType): RecallTraceResult[] {
236
+ return items.map((item) => ({
237
+ uri: item.uri,
238
+ resourceType,
239
+ category: item.category,
240
+ score: item.score,
241
+ level: item.level,
242
+ abstractPreview: preview(item.abstract ?? item.overview, 240),
243
+ resultType: resourceType === "resource" ? "resource" : "memory",
244
+ }));
245
+ }
246
+
247
+ function boundTraceQuery(query: string, maxChars: number): { query: string; queryTruncated?: boolean } {
248
+ if (query.length <= maxChars) {
249
+ return { query };
250
+ }
251
+ return { query: query.slice(0, maxChars), queryTruncated: true };
252
+ }
253
+
254
+ function runtimeFlag(runtimeContext: unknown, key: string): unknown {
255
+ return runtimeContext && typeof runtimeContext === "object"
256
+ ? (runtimeContext as Record<string, unknown>)[key]
257
+ : undefined;
258
+ }
259
+
260
+ export function isCronSession(sessionKey?: string, runtimeContext?: unknown): boolean {
261
+ return Boolean(
262
+ sessionKey?.includes(":cron:") ||
263
+ runtimeFlag(runtimeContext, "isCron") === true ||
264
+ runtimeFlag(runtimeContext, "automationKind") === "cron",
265
+ );
266
+ }
267
+
268
+ export function shouldRecallAgentExperience(input: {
269
+ latestUserText: string;
270
+ sessionKey?: string;
271
+ runtimeContext?: unknown;
272
+ triggerHint?: ExperienceRecallTrigger;
273
+ minQueryChars?: number;
274
+ isBypassed?: boolean;
275
+ }): ExperienceRecallDecision {
276
+ const text = sanitizeUserTextForCapture(input.latestUserText).trim();
277
+ const minQueryChars = input.minQueryChars ?? 12;
278
+
279
+ if (input.isBypassed) {
280
+ return { recall: false, score: 0, reason: "session_bypassed" };
281
+ }
282
+ if (!text || text.length < minQueryChars) {
283
+ return { recall: false, score: 0, reason: "query_too_short" };
284
+ }
285
+ if (/<openviking-context\b/i.test(input.latestUserText)) {
286
+ return { recall: false, score: 0, reason: "already_injected" };
287
+ }
288
+
289
+ const trigger = input.triggerHint ?? (isCronSession(input.sessionKey, input.runtimeContext) ? "cron_start" : "task_start");
290
+ if (trigger !== "task_start") {
291
+ return { recall: true, trigger, score: 99, reason: "forced_trigger" };
292
+ }
293
+
294
+ let score = 0;
295
+ if (WRITE_OR_EFFECT_RE.test(text)) score += 3;
296
+ if (EXECUTION_RE.test(text)) score += 2;
297
+ if (FAILURE_RE.test(text)) score += 2;
298
+ if (ENGINEERING_OBJECT_RE.test(text)) score += 2;
299
+ if (EXPERIENCE_INTENT_RE.test(text)) score += 1;
300
+
301
+ if (CASUAL_RE.test(text)) score -= 3;
302
+ if (QUESTION_ONLY_RE.test(text) && !ENGINEERING_OBJECT_RE.test(text) && !EXECUTION_RE.test(text)) {
303
+ score -= 2;
304
+ }
305
+
306
+ if (score >= 3) {
307
+ return { recall: true, trigger: "task_start", score, reason: "task_execution" };
308
+ }
309
+ return { recall: false, score, reason: score < 0 ? "non_execution" : "below_threshold" };
310
+ }
311
+
188
312
  export async function buildAutoRecallContext(params: {
189
313
  cfg: Required<MemoryOpenVikingConfig>;
314
+ queryConfig?: EffectiveQueryConfig;
190
315
  client: OpenVikingClient;
191
316
  agentId: string;
192
317
  queryText: string;
193
318
  logger: Logger;
194
319
  verbose?: (message: string) => void;
320
+ traceRecorder?: { record(entry: RecallTraceEntry): void; recordAndFlush?: (entry: RecallTraceEntry) => Promise<unknown> };
321
+ sessionId?: string;
322
+ sessionKey?: string;
323
+ ovSessionId?: string;
324
+ rawUserTextPreview?: string;
325
+ queryTruncated?: boolean;
326
+ resourceTypes?: RecallResourceType[];
195
327
  }): Promise<{ block?: string; memoryCount: number; estimatedTokens: number }> {
196
328
  const { cfg, client, agentId, queryText, logger, verbose } = params;
329
+ const queryConfig = params.queryConfig;
197
330
 
198
331
  if (!cfg.autoRecall || queryText.length < 5) {
199
332
  return { memoryCount: 0, estimatedTokens: 0 };
@@ -207,36 +340,89 @@ export async function buildAutoRecallContext(params: {
207
340
 
208
341
  return withTimeout(
209
342
  (async () => {
210
- const candidateLimit = Math.max(cfg.recallLimit * 4, 20);
211
- const autoRecallPromises: Promise<FindResult>[] = [
212
- client.find(queryText, {
213
- targetUri: "viking://user/memories",
214
- limit: candidateLimit,
215
- scoreThreshold: 0,
216
- }, agentId),
217
- client.find(queryText, {
218
- targetUri: "viking://agent/memories",
343
+ const candidateLimit = queryConfig?.candidateLimit ?? Math.max(cfg.recallLimit * 4, 20);
344
+ const scoreThreshold = queryConfig?.scoreThreshold ?? cfg.recallScoreThreshold;
345
+ const recallLimit = queryConfig?.recallLimit ?? cfg.recallLimit;
346
+ const maxInjectedChars = queryConfig?.maxInjectedChars ?? cfg.recallMaxInjectedChars;
347
+ const recallPreferAbstract = queryConfig?.recallPreferAbstract ?? cfg.recallPreferAbstract;
348
+ const searchPlan = resolveRecallSearchPlan(params.resourceTypes ?? queryConfig?.resourceTypes ?? cfg.recallTargetTypes, {
349
+ ovSessionId: params.ovSessionId,
350
+ agentId,
351
+ });
352
+ const traceSearches: RecallTraceEntry["searches"] = searchPlan.skipped.map((skipped) => ({
353
+ resourceType: skipped.resourceType,
354
+ limit: candidateLimit,
355
+ scoreThreshold: 0,
356
+ durationMs: 0,
357
+ total: 0,
358
+ results: [],
359
+ error: skipped.reason,
360
+ }));
361
+ const autoRecallPromises: Promise<{
362
+ resourceType: RecallResourceType;
363
+ targetUri?: string;
364
+ result?: FindResult;
365
+ durationMs: number;
366
+ }>[] = searchPlan.searches.map(async (search) => {
367
+ const start = Date.now();
368
+ const result = await client.find(queryText, {
369
+ targetUri: search.targetUri,
219
370
  limit: candidateLimit,
220
371
  scoreThreshold: 0,
221
- }, agentId),
222
- ];
223
- if (cfg.recallResources) {
224
- autoRecallPromises.push(
225
- client.find(queryText, {
226
- targetUri: "viking://resources",
227
- limit: candidateLimit,
228
- scoreThreshold: 0,
229
- }, agentId),
230
- );
231
- }
372
+ contextType: search.contextType,
373
+ actorPeerId: agentId,
374
+ });
375
+ return {
376
+ resourceType: search.resourceType,
377
+ targetUri: search.targetUri,
378
+ result,
379
+ durationMs: Date.now() - start,
380
+ };
381
+ });
232
382
  const autoRecallSettled = await Promise.allSettled(autoRecallPromises);
233
383
 
234
384
  const allMemories: FindResultItem[] = [];
235
385
  for (const s of autoRecallSettled) {
236
386
  if (s.status === "fulfilled") {
237
- allMemories.push(...(s.value.memories ?? []), ...(s.value.resources ?? []));
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
+ });
238
412
  } else {
239
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
+ });
240
426
  }
241
427
  }
242
428
 
@@ -246,11 +432,54 @@ export async function buildAutoRecallContext(params: {
246
432
  const leafOnly = uniqueMemories.filter((m) => !m.level || m.level === 2);
247
433
  const processed = postProcessMemories(leafOnly, {
248
434
  limit: candidateLimit,
249
- scoreThreshold: cfg.recallScoreThreshold,
435
+ scoreThreshold,
250
436
  });
251
- const memories = pickMemoriesForInjection(processed, cfg.recallLimit, queryText);
437
+ const memories = pickMemoriesForInjection(processed, recallLimit, queryText, scoreThreshold, queryConfig ? {
438
+ weights: queryConfig.rankingWeights,
439
+ categoryWeights: queryConfig.categoryWeights,
440
+ resourceTypeWeights: queryConfig.resourceTypeWeights,
441
+ } : undefined);
442
+
443
+ const recordTrace = async (injectedMemories: FindResultItem[], injectedCount: number, estimatedTokens?: number) => {
444
+ const entry: RecallTraceEntry = {
445
+ schemaVersion: "1.0",
446
+ traceId: newTraceId(),
447
+ ts: Date.now(),
448
+ sessionId: params.sessionId,
449
+ sessionKey: params.sessionKey,
450
+ ovSessionId: params.ovSessionId,
451
+ agentId,
452
+ source: "auto_recall",
453
+ operationType: "semantic_find",
454
+ resourceTypes: searchPlan.resourceTypes,
455
+ trigger: {
456
+ rawUserTextPreview: params.rawUserTextPreview,
457
+ ...boundTraceQuery(queryText, cfg.traceRecallQueryMaxChars),
458
+ derivedKeywords: [],
459
+ queryTruncated: params.queryTruncated || queryText.length > cfg.traceRecallQueryMaxChars,
460
+ },
461
+ searches: traceSearches,
462
+ selected: injectedMemories.map((memory) => ({
463
+ uri: memory.uri,
464
+ resourceType: memory.uri.startsWith("viking://resources") ? "resource" : undefined,
465
+ category: memory.category,
466
+ score: memory.score,
467
+ abstractPreview: preview(memory.abstract ?? memory.overview, cfg.traceRecallPreviewChars),
468
+ injected: true,
469
+ })),
470
+ stats: {
471
+ candidateCount: allMemories.length,
472
+ selectedCount: injectedMemories.length,
473
+ injectedCount,
474
+ estimatedTokens,
475
+ },
476
+ };
477
+ // Trace persistence is diagnostic best-effort; never put JSONL flush latency on the auto-recall critical path.
478
+ params.traceRecorder?.record(entry);
479
+ };
252
480
 
253
481
  if (memories.length === 0) {
482
+ await recordTrace([], 0, 0);
254
483
  return { memoryCount: 0, estimatedTokens: 0 };
255
484
  }
256
485
 
@@ -258,27 +487,29 @@ export async function buildAutoRecallContext(params: {
258
487
  memories,
259
488
  (uri) => client.read(uri, agentId),
260
489
  {
261
- recallPreferAbstract: cfg.recallPreferAbstract,
262
- recallMaxInjectedChars: cfg.recallMaxInjectedChars,
490
+ recallPreferAbstract,
491
+ recallMaxInjectedChars: maxInjectedChars,
263
492
  includeUri: true,
264
493
  },
265
494
  );
266
495
 
267
496
  if (memoryLines.length === 0) {
268
497
  verbose?.(
269
- `openviking: skipping auto-recall injection; no complete memories fit maxInjectedChars=${cfg.recallMaxInjectedChars}`,
498
+ `openviking: skipping auto-recall injection; no complete memories fit maxInjectedChars=${maxInjectedChars}`,
270
499
  );
500
+ await recordTrace([], 0, 0);
271
501
  return { memoryCount: 0, estimatedTokens: 0 };
272
502
  }
273
503
 
274
504
  const block = buildRecallContextBlock(memoryLines);
275
505
  verbose?.(
276
- `openviking: injecting ${memoryLines.length} memories (${block.length} chars, ~${estimatedTokens} tokens, maxInjectedChars=${cfg.recallMaxInjectedChars})`,
506
+ `openviking: injecting ${memoryLines.length} memories (${block.length} chars, ~${estimatedTokens} tokens, maxInjectedChars=${maxInjectedChars})`,
277
507
  );
278
508
  verbose?.(
279
509
  `openviking: inject-detail ${toJsonLog({ count: memories.length, memories: summarizeInjectionMemories(memories) })}`,
280
510
  );
281
511
 
512
+ await recordTrace(memories.slice(0, memoryLines.length), memoryLines.length, estimatedTokens);
282
513
  return { block, memoryCount: memoryLines.length, estimatedTokens };
283
514
  })(),
284
515
  AUTO_RECALL_TIMEOUT_MS,