@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/commands/setup.ts CHANGED
@@ -56,6 +56,32 @@ function readCompatRangeFromManifest(): { min: string; max: string } {
56
56
 
57
57
  const PLUGIN_VERSION = readPluginVersion();
58
58
  const { min: COMPATIBLE_SERVER_MIN, max: COMPATIBLE_SERVER_MAX } = readCompatRangeFromManifest();
59
+ const CONFIG_KEYS_TO_PRESERVE = [
60
+ "targetUri",
61
+ "timeoutMs",
62
+ "autoCapture",
63
+ "captureMode",
64
+ "captureMaxLength",
65
+ "autoRecall",
66
+ "recallResources",
67
+ "recallTargetTypes",
68
+ "recallLimit",
69
+ "recallScoreThreshold",
70
+ "recallMaxInjectedChars",
71
+ "recallMaxContentChars",
72
+ "recallPreferAbstract",
73
+ "recallTokenBudget",
74
+ "commitTokenThreshold",
75
+ "commitKeepRecentCount",
76
+ "bypassSessionPatterns",
77
+ "emitStandardDiagnostics",
78
+ "logFindRequests",
79
+ ] as const;
80
+
81
+ type PeerRole = "none" | "assistant" | "person";
82
+ const DEFAULT_SETUP_PEER_ROLE: PeerRole = "assistant";
83
+ type RecallTargetType = "resource" | "user" | "agent";
84
+ const ALLOWED_RECALL_TARGET_TYPES = ["resource", "user", "agent"] as const;
59
85
 
60
86
  type CommandProgram = {
61
87
  command: (name: string) => CommandBuilder;
@@ -81,29 +107,129 @@ function maskKey(key: string): string {
81
107
  return `${key.slice(0, 4)}...${key.slice(-4)}`;
82
108
  }
83
109
 
84
- function isValidAgentPrefixInput(value: string): boolean {
110
+ function isValidPeerPrefixInput(value: string): boolean {
85
111
  const trimmed = value.trim();
86
112
  return !trimmed || /^[a-zA-Z0-9_-]+$/.test(trimmed);
87
113
  }
88
114
 
89
- async function askAgentPrefix(
115
+ function normalizePeerRole(value: unknown): PeerRole | undefined {
116
+ if (typeof value !== "string") return undefined;
117
+ const role = value.trim().toLowerCase();
118
+ if (role === "none" || role === "assistant" || role === "person") return role;
119
+ return undefined;
120
+ }
121
+
122
+ function resolveExistingPeerRole(existing: Record<string, unknown> | null | undefined): PeerRole {
123
+ const explicit = normalizePeerRole(existing?.peer_role);
124
+ if (explicit) return explicit;
125
+ return DEFAULT_SETUP_PEER_ROLE;
126
+ }
127
+
128
+ function resolveExistingPeerPrefix(existing: Record<string, unknown> | null | undefined): string {
129
+ const value = existing?.peer_prefix;
130
+ if (typeof value !== "string" || !value.trim()) return "";
131
+ const trimmed = value.trim();
132
+ return trimmed === "default" ? "" : trimmed;
133
+ }
134
+
135
+ function resolveSetupPeerRole(value: unknown): PeerRole {
136
+ if (value === undefined) return DEFAULT_SETUP_PEER_ROLE;
137
+ const role = normalizePeerRole(value);
138
+ if (role) return role;
139
+ throw new Error('peer_role must be "none", "assistant", or "person"');
140
+ }
141
+
142
+ function normalizeSetupRecallTargetTypes(value: unknown): RecallTargetType[] | undefined {
143
+ if (value === undefined || value === null || value === "") {
144
+ return undefined;
145
+ }
146
+ const entries = Array.isArray(value)
147
+ ? value
148
+ : typeof value === "string"
149
+ ? value.split(/[,\n]/)
150
+ : [];
151
+ const seen = new Set<RecallTargetType>();
152
+ const normalized: RecallTargetType[] = [];
153
+ const unknown: string[] = [];
154
+
155
+ for (const raw of entries) {
156
+ if (typeof raw !== "string") {
157
+ continue;
158
+ }
159
+ const entry = raw.trim();
160
+ if (!entry) {
161
+ continue;
162
+ }
163
+ if ((ALLOWED_RECALL_TARGET_TYPES as readonly string[]).includes(entry)) {
164
+ const typed = entry as RecallTargetType;
165
+ if (!seen.has(typed)) {
166
+ seen.add(typed);
167
+ normalized.push(typed);
168
+ }
169
+ } else {
170
+ unknown.push(entry);
171
+ }
172
+ }
173
+
174
+ if (unknown.length > 0) {
175
+ throw new Error(`recall-target-types contains unknown resource types: ${unknown.join(", ")}`);
176
+ }
177
+ return normalized.length > 0 ? normalized : undefined;
178
+ }
179
+
180
+ function preserveCurrentConfig(existing: Record<string, unknown> | null | undefined) {
181
+ const config: Record<string, unknown> = {};
182
+ if (!existing) {
183
+ return config;
184
+ }
185
+ for (const key of CONFIG_KEYS_TO_PRESERVE) {
186
+ if (key in existing) {
187
+ config[key] = existing[key];
188
+ }
189
+ }
190
+ return config;
191
+ }
192
+
193
+ async function askPeerRole(
194
+ zh: boolean,
195
+ q: (prompt: string, def?: string) => Promise<string>,
196
+ defaultValue: PeerRole,
197
+ ): Promise<PeerRole> {
198
+ while (true) {
199
+ const value = await q(
200
+ tr(zh, "Peer Role (none/assistant/person)", "Peer Role(none/assistant/person)"),
201
+ defaultValue,
202
+ );
203
+ const role = normalizePeerRole(value);
204
+ if (role) return role;
205
+ console.log(
206
+ ` ✗ ${tr(
207
+ zh,
208
+ 'Peer Role must be "none", "assistant", or "person".',
209
+ 'Peer Role 必须是 "none"、"assistant" 或 "person"。',
210
+ )}`,
211
+ );
212
+ }
213
+ }
214
+
215
+ async function askPeerPrefix(
90
216
  zh: boolean,
91
217
  q: (prompt: string, def?: string) => Promise<string>,
92
218
  defaultValue: string,
93
219
  ): Promise<string> {
94
220
  while (true) {
95
221
  const value = (await q(
96
- tr(zh, "Agent Prefix (optional)", "Agent Prefix(可选)"),
222
+ tr(zh, "Peer Prefix (optional)", "Peer Prefix(可选)"),
97
223
  defaultValue,
98
224
  )).trim();
99
- if (isValidAgentPrefixInput(value)) {
225
+ if (isValidPeerPrefixInput(value)) {
100
226
  return value;
101
227
  }
102
228
  console.log(
103
229
  ` ✗ ${tr(
104
230
  zh,
105
- "Agent Prefix may only contain letters, digits, underscores, and hyphens, or be empty.",
106
- "Agent Prefix 只能包含字母、数字、下划线和连字符,或留空。",
231
+ "Peer Prefix may only contain letters, digits, underscores, and hyphens, or be empty.",
232
+ "Peer Prefix 只能包含字母、数字、下划线和连字符,或留空。",
107
233
  )}`,
108
234
  );
109
235
  }
@@ -366,9 +492,11 @@ type SetupResult = {
366
492
  mode: string;
367
493
  baseUrl: string;
368
494
  apiKey?: string;
369
- agent_prefix?: string;
495
+ peer_role?: PeerRole;
496
+ peer_prefix?: string;
370
497
  accountId?: string;
371
498
  userId?: string;
499
+ recallTargetTypes?: string[];
372
500
  };
373
501
  health?: HealthResult;
374
502
  keyProbe?: ApiKeyProbeResult;
@@ -388,7 +516,8 @@ type StatusResult = {
388
516
  mode: string;
389
517
  baseUrl: string;
390
518
  hasApiKey: boolean;
391
- agent_prefix?: string;
519
+ peer_role: PeerRole;
520
+ peer_prefix?: string;
392
521
  hasAccountId: boolean;
393
522
  hasUserId: boolean;
394
523
  };
@@ -449,21 +578,23 @@ export function registerSetupCli(api: any): void {
449
578
  .option("--zh", "Chinese prompts")
450
579
  .option("--base-url <url>", "OpenViking server URL (enables non-interactive mode)")
451
580
  .option("--api-key <key>", "API key for authentication")
452
- .option("--agent-prefix <prefix>", "Agent routing prefix for namespace isolation")
581
+ .option("--peer-role <role>", "Peer ID role: none, assistant, or person")
582
+ .option("--peer-prefix <prefix>", "Prefix for assistant peer_id values")
453
583
  .option("--account-id <id>", "Account ID (required for root API keys)")
454
584
  .option("--user-id <id>", "User ID (required for root API keys)")
585
+ .option("--recall-target-types <types>", "Comma-separated recall target types: user, agent, resource")
455
586
  .option("--allow-offline", "Allow config write even if server is unreachable")
456
587
  .option("--force-slot", "Replace existing contextEngine slot even if owned by another plugin")
457
588
  .option("--json", "Output result as JSON (machine-readable)")
458
589
  .action(async (...args: unknown[]) => {
459
590
  const options = (args[0] ?? {}) as Record<string, unknown>;
460
591
  const {
461
- reconfigure, zh: zhOpt, baseUrl, apiKey, agentPrefix,
462
- accountId, userId, allowOffline, forceSlot, json: jsonOpt,
592
+ reconfigure, zh: zhOpt, baseUrl, apiKey, peerRole, peerPrefix,
593
+ accountId, userId, recallTargetTypes, allowOffline, forceSlot, json: jsonOpt,
463
594
  } = options as {
464
595
  reconfigure?: boolean; zh?: boolean; baseUrl?: string;
465
- apiKey?: string; agentPrefix?: string; accountId?: string;
466
- userId?: string; allowOffline?: boolean; forceSlot?: boolean;
596
+ apiKey?: string; peerRole?: string; peerPrefix?: string; accountId?: string;
597
+ userId?: string; recallTargetTypes?: string; allowOffline?: boolean; forceSlot?: boolean;
467
598
  json?: boolean;
468
599
  };
469
600
  const zh = detectLangZh(options);
@@ -475,9 +606,11 @@ export function registerSetupCli(api: any): void {
475
606
  const result = await setupNonInteractive(configPath, {
476
607
  baseUrl: baseUrl!,
477
608
  apiKey,
478
- agentPrefix,
609
+ peerRole,
610
+ peerPrefix,
479
611
  accountId,
480
612
  userId,
613
+ recallTargetTypes: normalizeSetupRecallTargetTypes(recallTargetTypes),
481
614
  allowOffline: !!allowOffline,
482
615
  forceSlot: !!forceSlot,
483
616
  });
@@ -533,7 +666,9 @@ export function registerSetupCli(api: any): void {
533
666
  console.log(` mode: ${existing.mode}`);
534
667
  console.log(` baseUrl: ${existing.baseUrl ?? DEFAULT_REMOTE_URL}`);
535
668
  if (existing.apiKey) console.log(` apiKey: ${maskKey(String(existing.apiKey))}`);
536
- if (existing.agent_prefix) console.log(` agent_prefix: ${existing.agent_prefix}`);
669
+ console.log(` peer_role: ${resolveExistingPeerRole(existing)}`);
670
+ const existingPeerPrefix = resolveExistingPeerPrefix(existing);
671
+ if (existingPeerPrefix) console.log(` peer_prefix: ${existingPeerPrefix}`);
537
672
  console.log("");
538
673
  console.log(tr(
539
674
  zh,
@@ -651,10 +786,25 @@ async function runRemoteCheck(
651
786
 
652
787
  async function setupNonInteractive(
653
788
  configPath: string,
654
- params: { baseUrl: string; apiKey?: string; agentPrefix?: string; accountId?: string; userId?: string; allowOffline?: boolean; forceSlot?: boolean },
789
+ params: {
790
+ baseUrl: string;
791
+ apiKey?: string;
792
+ peerRole?: string;
793
+ peerPrefix?: string;
794
+ accountId?: string;
795
+ userId?: string;
796
+ recallTargetTypes?: string[];
797
+ allowOffline?: boolean;
798
+ forceSlot?: boolean;
799
+ },
655
800
  ): Promise<SetupResult> {
656
801
  try {
657
- const { baseUrl, apiKey, agentPrefix, accountId, userId, allowOffline, forceSlot } = params;
802
+ const { baseUrl, apiKey, peerPrefix, accountId, userId, recallTargetTypes, allowOffline, forceSlot } = params;
803
+ const resolvedPeerRole = resolveSetupPeerRole(params.peerRole);
804
+ const resolvedPeerPrefix = (peerPrefix ?? "").trim();
805
+ if (!isValidPeerPrefixInput(resolvedPeerPrefix)) {
806
+ throw new Error("peer_prefix may only contain letters, digits, underscores, and hyphens, or be empty");
807
+ }
658
808
 
659
809
  // Phase 1: validate connectivity and key type BEFORE writing config
660
810
  const health = await checkServiceHealth(baseUrl, apiKey);
@@ -694,9 +844,13 @@ async function setupNonInteractive(
694
844
  // Phase 2: all checks passed (or --allow-offline), write config and activate slot
695
845
  const pluginCfg: Record<string, unknown> = { mode: "remote", baseUrl };
696
846
  if (apiKey) pluginCfg.apiKey = apiKey;
697
- if (agentPrefix) pluginCfg.agent_prefix = agentPrefix;
847
+ pluginCfg.peer_role = resolvedPeerRole;
848
+ if (resolvedPeerPrefix) pluginCfg.peer_prefix = resolvedPeerPrefix;
698
849
  if (accountId) pluginCfg.accountId = accountId;
699
850
  if (userId) pluginCfg.userId = userId;
851
+ if (recallTargetTypes && recallTargetTypes.length > 0) {
852
+ pluginCfg.recallTargetTypes = recallTargetTypes;
853
+ }
700
854
 
701
855
  writeConfig(configPath, pluginCfg);
702
856
  const slot = activateContextEngineSlot(configPath, !!forceSlot);
@@ -709,9 +863,11 @@ async function setupNonInteractive(
709
863
  mode: "remote",
710
864
  baseUrl,
711
865
  ...(apiKey ? { apiKey: maskKey(apiKey) } : {}),
712
- ...(agentPrefix ? { agent_prefix: agentPrefix } : {}),
866
+ peer_role: resolvedPeerRole,
867
+ ...(resolvedPeerPrefix ? { peer_prefix: resolvedPeerPrefix } : {}),
713
868
  ...(accountId ? { accountId } : {}),
714
869
  ...(userId ? { userId } : {}),
870
+ ...(recallTargetTypes && recallTargetTypes.length > 0 ? { recallTargetTypes } : {}),
715
871
  },
716
872
  health,
717
873
  keyProbe,
@@ -727,9 +883,11 @@ async function setupNonInteractive(
727
883
  mode: "remote",
728
884
  baseUrl,
729
885
  ...(apiKey ? { apiKey: maskKey(apiKey) } : {}),
730
- ...(agentPrefix ? { agent_prefix: agentPrefix } : {}),
886
+ peer_role: resolvedPeerRole,
887
+ ...(resolvedPeerPrefix ? { peer_prefix: resolvedPeerPrefix } : {}),
731
888
  ...(accountId ? { accountId } : {}),
732
889
  ...(userId ? { userId } : {}),
890
+ ...(recallTargetTypes && recallTargetTypes.length > 0 ? { recallTargetTypes } : {}),
733
891
  },
734
892
  health,
735
893
  keyProbe,
@@ -754,9 +912,11 @@ function printSetupResult(zh: boolean, result: SetupResult): void {
754
912
  console.log(` mode: ${result.config.mode}`);
755
913
  console.log(` baseUrl: ${result.config.baseUrl}`);
756
914
  if (result.config.apiKey) console.log(` apiKey: ${result.config.apiKey}`);
757
- if (result.config.agent_prefix) console.log(` agent_prefix: ${result.config.agent_prefix}`);
915
+ console.log(` peer_role: ${result.config.peer_role ?? DEFAULT_SETUP_PEER_ROLE}`);
916
+ if (result.config.peer_prefix) console.log(` peer_prefix: ${result.config.peer_prefix}`);
758
917
  if (result.config.accountId) console.log(` accountId: ${result.config.accountId}`);
759
918
  if (result.config.userId) console.log(` userId: ${result.config.userId}`);
919
+ if (result.config.recallTargetTypes) console.log(` recallTargetTypes: ${result.config.recallTargetTypes.join(",")}`);
760
920
  }
761
921
  console.log("");
762
922
  if (result.health?.ok) {
@@ -798,14 +958,15 @@ async function getStatus(configPath: string): Promise<StatusResult> {
798
958
  const health = await checkServiceHealth(baseUrl, apiKey);
799
959
  const keyProbe = health.ok ? await probeApiKeyType(baseUrl, apiKey) : undefined;
800
960
 
801
- const agentPrefix = existing.agent_prefix ?? existing.agentId;
961
+ const peerPrefix = resolveExistingPeerPrefix(existing);
802
962
  return {
803
963
  configured: true,
804
964
  config: {
805
965
  mode: String(existing.mode ?? "remote"),
806
966
  baseUrl,
807
967
  hasApiKey: !!existing.apiKey,
808
- ...(agentPrefix ? { agent_prefix: String(agentPrefix) } : {}),
968
+ peer_role: resolveExistingPeerRole(existing),
969
+ ...(peerPrefix ? { peer_prefix: peerPrefix } : {}),
809
970
  hasAccountId: !!existing.accountId,
810
971
  hasUserId: !!existing.userId,
811
972
  },
@@ -869,7 +1030,8 @@ function printStatus(zh: boolean, result: StatusResult): void {
869
1030
  console.log(` mode: ${result.config.mode}`);
870
1031
  console.log(` baseUrl: ${result.config.baseUrl}`);
871
1032
  console.log(` apiKey: ${result.config.hasApiKey ? "set" : "not set"}`);
872
- if (result.config.agent_prefix) console.log(` agent_prefix: ${result.config.agent_prefix}`);
1033
+ console.log(` peer_role: ${result.config.peer_role}`);
1034
+ if (result.config.peer_prefix) console.log(` peer_prefix: ${result.config.peer_prefix}`);
873
1035
  console.log(` accountId: ${result.config.hasAccountId ? "set" : "not set"}`);
874
1036
  console.log(` userId: ${result.config.hasUserId ? "set" : "not set"}`);
875
1037
  }
@@ -904,7 +1066,8 @@ async function setupRemote(
904
1066
  ? String(existing.baseUrl)
905
1067
  : DEFAULT_REMOTE_URL;
906
1068
  const defaultApiKey = existing?.apiKey ? String(existing.apiKey) : "";
907
- const defaultAgentPrefix = existing?.agent_prefix ? String(existing.agent_prefix) : "";
1069
+ const defaultPeerRole = resolveExistingPeerRole(existing);
1070
+ const defaultPeerPrefix = resolveExistingPeerPrefix(existing);
908
1071
 
909
1072
  const baseUrl = await q(tr(zh, "OpenViking server URL", "OpenViking 服务器地址"), defaultUrl);
910
1073
  const apiKey = await q(tr(zh, "API Key (optional)", "API Key(可选)"), defaultApiKey);
@@ -927,7 +1090,10 @@ async function setupRemote(
927
1090
  }
928
1091
  }
929
1092
 
930
- const agentPrefix = await askAgentPrefix(zh, q, defaultAgentPrefix);
1093
+ const peerRole = await askPeerRole(zh, q, defaultPeerRole);
1094
+ const peerPrefix = peerRole === "assistant"
1095
+ ? await askPeerPrefix(zh, q, defaultPeerPrefix)
1096
+ : "";
931
1097
 
932
1098
  console.log("");
933
1099
 
@@ -948,20 +1114,19 @@ async function setupRemote(
948
1114
  console.log("");
949
1115
 
950
1116
  const pluginCfg: Record<string, unknown> = {
951
- ...(existing ?? {}),
1117
+ ...preserveCurrentConfig(existing),
952
1118
  mode: "remote",
953
1119
  baseUrl,
954
1120
  };
955
1121
  if (apiKey) pluginCfg.apiKey = apiKey;
956
1122
  else delete pluginCfg.apiKey;
957
- if (agentPrefix) pluginCfg.agent_prefix = agentPrefix;
958
- else delete pluginCfg.agent_prefix;
1123
+ pluginCfg.peer_role = peerRole;
1124
+ if (peerPrefix) pluginCfg.peer_prefix = peerPrefix;
1125
+ else delete pluginCfg.peer_prefix;
959
1126
  if (accountId) pluginCfg.accountId = accountId;
960
1127
  else delete pluginCfg.accountId;
961
1128
  if (userId) pluginCfg.userId = userId;
962
1129
  else delete pluginCfg.userId;
963
- delete pluginCfg.configPath;
964
- delete pluginCfg.port;
965
1130
 
966
1131
  writeConfig(configPath, pluginCfg);
967
1132
 
@@ -971,7 +1136,8 @@ async function setupRemote(
971
1136
  console.log(` ${tr(zh, "mode:", "模式:")} remote`);
972
1137
  console.log(` baseUrl: ${baseUrl}`);
973
1138
  if (apiKey) console.log(` apiKey: ${maskKey(apiKey)}`);
974
- if (agentPrefix) console.log(` agent_prefix: ${agentPrefix}`);
1139
+ console.log(` peer_role: ${peerRole}`);
1140
+ if (peerPrefix) console.log(` peer_prefix: ${peerPrefix}`);
975
1141
  if (accountId) console.log(` accountId: ${accountId}`);
976
1142
  if (userId) console.log(` userId: ${userId}`);
977
1143
  printSlotResult(zh, slotResult);
@@ -985,7 +1151,8 @@ async function setupRemote(
985
1151
 
986
1152
  export const __test__ = {
987
1153
  isLegacyLocalMode,
988
- isValidAgentPrefixInput,
1154
+ isValidPeerPrefixInput,
1155
+ normalizeSetupRecallTargetTypes,
989
1156
  activateContextEngineSlot,
990
1157
  isContextEngineSlotActive,
991
1158
  getStatus,
@@ -0,0 +1,10 @@
1
+ {
2
+ "features": {
3
+ "openviking.260610": {
4
+ "enable": true,
5
+ "minPluginVersion": "2026.6.10",
6
+ "minOpenclawVersion": "2026.4.8",
7
+ "editions": ["arkclaw_enterprise", "arkclaw"]
8
+ }
9
+ }
10
+ }