@openviking/openclaw-plugin 2026.8.19 → 2026.8.28

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 CHANGED
@@ -269,7 +269,7 @@ They serve different roles:
269
269
  Resource and skill imports are intentionally separate because they land in different OpenViking namespaces and use different server APIs:
270
270
 
271
271
  - resources go through `/api/v1/resources` and land under `viking://resources/...`
272
- - skills go through `/api/v1/skills` and land under `viking://user/skills/...`
272
+ - skills go through `/api/v1/skills` and land under `viking://user/<uid>/skills/...`
273
273
 
274
274
  The plugin also registers explicit slash commands for manual imports:
275
275
 
@@ -277,7 +277,7 @@ The plugin also registers explicit slash commands for manual imports:
277
277
  /add-resource ./README.md --to viking://resources/openviking-readme --wait
278
278
  /add-skill ./skills/install-openviking-memory --wait
279
279
  /ov-search "OpenViking install" --uri viking://resources/openviking-readme
280
- /ov-search "memory install skill" --uri viking://user/skills
280
+ /ov-search "memory install skill" --uri viking://~/skills
281
281
  ```
282
282
 
283
283
  Resource import supports remote URLs, Git URLs, local files, local directories, and uploaded zip files. OpenViking's built-in parsers cover common documents and media such as Markdown, text, PDF, HTML, Word, PowerPoint, Excel, EPUB, images, audio, and video. Directory imports also accept common code, documentation, and config file extensions such as `.py`, `.js`, `.ts`, `.go`, `.rs`, `.java`, `.cpp`, `.json`, `.yaml`, `.toml`, `.csv`, `.rst`, `.proto`, `.tf`, and `.vue`.
package/client.ts CHANGED
@@ -36,7 +36,7 @@ export type FsListResult = FsListEntry[];
36
36
 
37
37
  export type CaptureMode = "semantic" | "keyword";
38
38
  function userSessionUri(sessionId: string): string {
39
- return `viking://user/sessions/${encodeURIComponent(sessionId)}`;
39
+ return `viking://~/sessions/${encodeURIComponent(sessionId)}`;
40
40
  }
41
41
 
42
42
  export type OpenVikingClientOptions = {
package/config.ts CHANGED
@@ -136,7 +136,7 @@ export type ParsedMemoryOpenVikingConfig = Required<
136
136
  };
137
137
 
138
138
  const DEFAULT_BASE_URL = "http://127.0.0.1:1933";
139
- const DEFAULT_TARGET_URI = "viking://user/memories";
139
+ 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;
package/dist/client.js CHANGED
@@ -8,7 +8,7 @@ import { Zip, ZipDeflate } from "fflate";
8
8
  import { defaultHttpTransport } from "./adapters/http-transport.js";
9
9
  import { defaultResourcePackager, } from "./adapters/resource-packager.js";
10
10
  function userSessionUri(sessionId) {
11
- return `viking://user/sessions/${encodeURIComponent(sessionId)}`;
11
+ return `viking://~/sessions/${encodeURIComponent(sessionId)}`;
12
12
  }
13
13
  const DEFAULT_WAIT_REQUEST_TIMEOUT_MS = 120_000;
14
14
  export const DEFAULT_PHASE2_POLL_TIMEOUT_MS = 300_000;
package/dist/config.js CHANGED
@@ -2,7 +2,7 @@ import { homedir } from "node:os";
2
2
  import { readFileSync } from "node:fs";
3
3
  import { getEnv } from "./runtime-utils.js";
4
4
  const DEFAULT_BASE_URL = "http://127.0.0.1:1933";
5
- const DEFAULT_TARGET_URI = "viking://user/memories";
5
+ const DEFAULT_TARGET_URI = "viking://~/memories";
6
6
  const DEFAULT_TIMEOUT_MS = 15000;
7
7
  const DEFAULT_CAPTURE_MODE = "semantic";
8
8
  const DEFAULT_CAPTURE_MAX_LENGTH = 24000;
@@ -181,9 +181,9 @@ function inferResourceType(item) {
181
181
  return "resource";
182
182
  if (item.uri.startsWith("viking://session/"))
183
183
  return "session";
184
- if (item.uri.startsWith("viking://user/skills") || item.uri.startsWith("viking://skills"))
184
+ if (item.uri.startsWith("viking://user/skills") || item.uri.startsWith("viking://~/skills") || item.uri.startsWith("viking://skills"))
185
185
  return "agent";
186
- if (item.uri.startsWith("viking://user/"))
186
+ if (item.uri.startsWith("viking://user/") || item.uri.startsWith("viking://~/"))
187
187
  return "user";
188
188
  return undefined;
189
189
  }
@@ -279,7 +279,7 @@ export function createOpenVikingQueryRuntime(deps) {
279
279
  };
280
280
  const [resourcesSettled, skillsSettled] = await Promise.allSettled([
281
281
  runSearch("viking://resources"),
282
- runSearch("viking://user/skills"),
282
+ runSearch("viking://~/skills"),
283
283
  ]);
284
284
  const successful = [];
285
285
  if (resourcesSettled.status === "fulfilled") {
@@ -57,9 +57,9 @@ function inferUriType(uri) {
57
57
  return "resource";
58
58
  if (uri.startsWith("viking://session/"))
59
59
  return "session";
60
- if (uri.startsWith("viking://user/skills") || uri.startsWith("viking://skills"))
60
+ if (uri.startsWith("viking://user/skills") || uri.startsWith("viking://~/skills") || uri.startsWith("viking://skills"))
61
61
  return "skill";
62
- if (uri.startsWith("viking://user/"))
62
+ if (uri.startsWith("viking://user/") || uri.startsWith("viking://~/"))
63
63
  return "user_memory";
64
64
  if (uri.includes("/archive") || uri.includes("/history/"))
65
65
  return "archive";
@@ -8,9 +8,11 @@ export function previewText(value, maxChars) {
8
8
  export function inferRecallResourceType(uri) {
9
9
  if (uri.startsWith("viking://resources"))
10
10
  return "resource";
11
- if (uri.startsWith("viking://user/skills") || uri.startsWith("viking://skills"))
11
+ // viking://~ is the home alias for the caller's own user space; configured target
12
+ // URIs use it, while server responses come back canonical as viking://user/<uid>/...
13
+ if (uri.startsWith("viking://user/skills") || uri.startsWith("viking://~/skills") || uri.startsWith("viking://skills"))
12
14
  return "agent";
13
- if (uri.startsWith("viking://user/"))
15
+ if (uri.startsWith("viking://user/") || uri === "viking://~" || uri.startsWith("viking://~/"))
14
16
  return "user";
15
17
  return undefined;
16
18
  }
@@ -1,6 +1,10 @@
1
1
  const MEMORY_URI_PATTERNS = [
2
2
  /^viking:\/\/user\/(?:[^/]+(?:\/agent\/[^/]+)?\/)?memories(?:\/|$)/,
3
3
  /^viking:\/\/user\/[^/]+\/peers\/[^/]+\/memories(?:\/|$)/,
4
+ // viking://~ is the home alias for the caller's own user space. Configured targets and
5
+ // model-supplied URIs may use it; server responses stay canonical viking://user/<uid>/...
6
+ /^viking:\/\/~\/memories(?:\/|$)/,
7
+ /^viking:\/\/~\/peers\/[^/]+\/memories(?:\/|$)/,
4
8
  /^viking:\/\/agent\/(?:[^/]+(?:\/user\/[^/]+)?\/)?memories(?:\/|$)/,
5
9
  ];
6
10
  export function isMemoryUri(uri) {
@@ -196,10 +196,14 @@ function isSessionNotFoundError(err) {
196
196
  return errorMessage.includes("[NOT_FOUND]") && errorMessage.includes("Session not found");
197
197
  }
198
198
  export async function assembleOpenVikingSession({ sessionId, sessionKey, messages, tokenBudget, runtimeContext, isMainAssemble, cfg, getClient, logger, resolveAgentId, rememberSessionAgentId, isBypassedSession, queryConfigStore, traceRecorder, diag, roughEstimate, messageDigest, extractAgentMessageText, hasAutoRecallBlock, prependRecallToLatestUserMessage, }) {
199
- const ovSessionId = openClawSessionToOvStorageId(sessionId, sessionKey);
200
- const sender = extractRuntimeSenderId(runtimeContext);
201
199
  const latestMessage = messages.at(-1);
202
200
  const isTransformContextAssemble = !isMainAssemble;
201
+ // Tool-loop calls do no recall work on non-user tails; avoid scanning the full history.
202
+ if (isTransformContextAssemble && latestMessage?.role !== "user") {
203
+ return { messages, estimatedTokens: 0 };
204
+ }
205
+ const ovSessionId = openClawSessionToOvStorageId(sessionId, sessionKey);
206
+ const sender = extractRuntimeSenderId(runtimeContext);
203
207
  const originalTokens = roughEstimate(messages);
204
208
  rememberSessionAgentId?.({
205
209
  sessionId,
@@ -220,16 +224,6 @@ export async function assembleOpenVikingSession({ sessionId, sessionKey, message
220
224
  return assemblePassthrough({ diag, ovSessionId, reason: "session_bypassed", liveMessages: messages, originalTokens });
221
225
  }
222
226
  if (isTransformContextAssemble) {
223
- if (latestMessage?.role !== "user") {
224
- return assemblePassthrough({
225
- diag,
226
- ovSessionId,
227
- reason: "transform_context_non_user_tail",
228
- liveMessages: messages,
229
- originalTokens,
230
- extra: { latestRole: latestMessage?.role ?? null },
231
- });
232
- }
233
227
  if (!cfg.autoRecall) {
234
228
  return assemblePassthrough({ diag, ovSessionId, reason: "transform_context_auto_recall_disabled", liveMessages: messages, originalTokens });
235
229
  }
package/memory-ranking.ts CHANGED
@@ -232,8 +232,8 @@ function lexicalOverlapBoost(tokens: string[], text: string, maxBoost = DEFAULT_
232
232
  function inferResourceType(item: FindResultItem): string | undefined {
233
233
  if (item.uri.startsWith("viking://resources")) return "resource";
234
234
  if (item.uri.startsWith("viking://session/")) return "session";
235
- if (item.uri.startsWith("viking://user/skills") || item.uri.startsWith("viking://skills")) return "agent";
236
- if (item.uri.startsWith("viking://user/")) return "user";
235
+ if (item.uri.startsWith("viking://user/skills") || item.uri.startsWith("viking://~/skills") || item.uri.startsWith("viking://skills")) return "agent";
236
+ if (item.uri.startsWith("viking://user/") || item.uri.startsWith("viking://~/")) return "user";
237
237
  return undefined;
238
238
  }
239
239
 
@@ -120,7 +120,7 @@
120
120
  },
121
121
  "targetUri": {
122
122
  "label": "Search Target URI",
123
- "placeholder": "viking://user/memories",
123
+ "placeholder": "viking://~/memories",
124
124
  "help": "Default OpenViking target URI for memory search"
125
125
  },
126
126
  "timeoutMs": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openviking/openclaw-plugin",
3
- "version": "2026.8.19",
3
+ "version": "2026.8.28",
4
4
  "displayName": "OpenViking",
5
5
  "description": "OpenClaw context-engine plugin — OpenViking AI memory, long-term memory, knowledge base, semantic search, RAG, context engine. 长期记忆 · 知识库 · 语义检索 · 上下文引擎",
6
6
  "type": "module",
@@ -386,7 +386,7 @@ export function createOpenVikingQueryRuntime<TQueryConfigContext>(deps: OpenViki
386
386
  };
387
387
  const [resourcesSettled, skillsSettled] = await Promise.allSettled([
388
388
  runSearch("viking://resources"),
389
- runSearch("viking://user/skills"),
389
+ runSearch("viking://~/skills"),
390
390
  ]);
391
391
  const successful: FindResult[] = [];
392
392
  if (resourcesSettled.status === "fulfilled") {
@@ -120,8 +120,8 @@ function toLimitedInteger(value: unknown, fallback: number, min: number, max: nu
120
120
  function inferUriType(uri: string): string {
121
121
  if (uri.startsWith("viking://resources") || uri.startsWith("viking://resource")) return "resource";
122
122
  if (uri.startsWith("viking://session/")) return "session";
123
- if (uri.startsWith("viking://user/skills") || uri.startsWith("viking://skills")) return "skill";
124
- if (uri.startsWith("viking://user/")) return "user_memory";
123
+ if (uri.startsWith("viking://user/skills") || uri.startsWith("viking://~/skills") || uri.startsWith("viking://skills")) return "skill";
124
+ if (uri.startsWith("viking://user/") || uri.startsWith("viking://~/")) return "user_memory";
125
125
  if (uri.includes("/archive") || uri.includes("/history/")) return "archive";
126
126
  return "unknown";
127
127
  }
@@ -10,8 +10,10 @@ export function previewText(value: unknown, maxChars: number): string | undefine
10
10
 
11
11
  export function inferRecallResourceType(uri: string): RecallResourceType | undefined {
12
12
  if (uri.startsWith("viking://resources")) return "resource";
13
- if (uri.startsWith("viking://user/skills") || uri.startsWith("viking://skills")) return "agent";
14
- if (uri.startsWith("viking://user/")) return "user";
13
+ // viking://~ is the home alias for the caller's own user space; configured target
14
+ // URIs use it, while server responses come back canonical as viking://user/<uid>/...
15
+ if (uri.startsWith("viking://user/skills") || uri.startsWith("viking://~/skills") || uri.startsWith("viking://skills")) return "agent";
16
+ if (uri.startsWith("viking://user/") || uri === "viking://~" || uri.startsWith("viking://~/")) return "user";
15
17
  return undefined;
16
18
  }
17
19
 
@@ -1,6 +1,10 @@
1
1
  const MEMORY_URI_PATTERNS = [
2
2
  /^viking:\/\/user\/(?:[^/]+(?:\/agent\/[^/]+)?\/)?memories(?:\/|$)/,
3
3
  /^viking:\/\/user\/[^/]+\/peers\/[^/]+\/memories(?:\/|$)/,
4
+ // viking://~ is the home alias for the caller's own user space. Configured targets and
5
+ // model-supplied URIs may use it; server responses stay canonical viking://user/<uid>/...
6
+ /^viking:\/\/~\/memories(?:\/|$)/,
7
+ /^viking:\/\/~\/peers\/[^/]+\/memories(?:\/|$)/,
4
8
  /^viking:\/\/agent\/(?:[^/]+(?:\/user\/[^/]+)?\/)?memories(?:\/|$)/,
5
9
  ];
6
10
 
@@ -441,10 +441,16 @@ export async function assembleOpenVikingSession({
441
441
  hasAutoRecallBlock,
442
442
  prependRecallToLatestUserMessage,
443
443
  }: AssembleOpenVikingSessionParams): Promise<AssembleOpenVikingSessionResult> {
444
- const ovSessionId = openClawSessionToOvStorageId(sessionId, sessionKey);
445
- const sender = extractRuntimeSenderId(runtimeContext);
446
444
  const latestMessage = messages.at(-1);
447
445
  const isTransformContextAssemble = !isMainAssemble;
446
+
447
+ // Tool-loop calls do no recall work on non-user tails; avoid scanning the full history.
448
+ if (isTransformContextAssemble && latestMessage?.role !== "user") {
449
+ return { messages, estimatedTokens: 0 };
450
+ }
451
+
452
+ const ovSessionId = openClawSessionToOvStorageId(sessionId, sessionKey);
453
+ const sender = extractRuntimeSenderId(runtimeContext);
448
454
  const originalTokens = roughEstimate(messages);
449
455
 
450
456
  rememberSessionAgentId?.({
@@ -468,16 +474,6 @@ export async function assembleOpenVikingSession({
468
474
  }
469
475
 
470
476
  if (isTransformContextAssemble) {
471
- if (latestMessage?.role !== "user") {
472
- return assemblePassthrough({
473
- diag,
474
- ovSessionId,
475
- reason: "transform_context_non_user_tail",
476
- liveMessages: messages,
477
- originalTokens,
478
- extra: { latestRole: latestMessage?.role ?? null },
479
- });
480
- }
481
477
  if (!cfg.autoRecall) {
482
478
  return assemblePassthrough({ diag, ovSessionId, reason: "transform_context_auto_recall_disabled", liveMessages: messages, originalTokens });
483
479
  }
@@ -590,7 +590,7 @@ These are the keys under `plugins.entries.openviking.config` in `openclaw.json`.
590
590
  | `peer_prefix` | `""` | Optional prefix for assistant `peer_id` / actor peer values when `peer_role=assistant`. Letters / digits / `_` / `-`. |
591
591
  | `accountId` | — | Required when `apiKey` is a root key. |
592
592
  | `userId` | — | Required when `apiKey` is a root key. |
593
- | `targetUri` | `viking://user/memories` | Default search scope URI. |
593
+ | `targetUri` | `viking://~/memories` | Default search scope URI. |
594
594
  | `timeoutMs` | (plugin default) | HTTP timeout for OpenViking calls. |
595
595
  | `autoCapture` | `true` | Auto-append turn messages to the OpenViking session at `afterTurn`; extraction runs only after a threshold commit, `/compact`, or explicit `memory_store`. |
596
596
  | `captureMode` | `"semantic"` | Filter mode used by the server-side extraction pipeline: `semantic` or `keyword`. |
@@ -67,7 +67,7 @@ Core config lives under `plugins.entries.openviking.config`:
67
67
  | `peer_role` | `assistant` | Peer identity mode: `none`, `assistant`, or `person`. Session messages use body `peer_id`; data-plane recall/search uses `X-OpenViking-Actor-Peer`. |
68
68
  | `peer_prefix` | empty | Optional prefix for assistant `peer_id` / actor peer values when `peer_role=assistant`. |
69
69
  | `accountId` / `userId` | empty | Advanced tenant identity headers for root-key or trusted deployments. |
70
- | `targetUri` | `viking://user/memories` | Default search scope for legacy targeted memory search. |
70
+ | `targetUri` | `viking://~/memories` | Default search scope for legacy targeted memory search. |
71
71
  | `autoCapture` | `true` | Append sanitized turn text to OpenViking sessions. |
72
72
  | `captureMode` | `semantic` | `semantic` or `keyword`; affects server-side extraction filtering. |
73
73
  | `captureMaxLength` | `24000` | Max sanitized text length per captured turn. |
@@ -196,7 +196,7 @@ The current OpenClaw tool exposes the parameters above. The underlying client al
196
196
 
197
197
  ### `add_skill`
198
198
 
199
- Import Agent Skills into `viking://user/skills/...`.
199
+ Import Agent Skills into `viking://~/skills/...`.
200
200
 
201
201
  | Parameter | Required | Description |
202
202
  |---|---|---|
@@ -50,7 +50,7 @@ context or deeper intent analysis is useful.
50
50
  3. Search only the current user's Experience root:
51
51
 
52
52
  ```text
53
- viking://user/memories/experiences
53
+ viking://~/memories/experiences
54
54
  ```
55
55
 
56
56
  For tools using MCP-style parameters, set `target_uri` to this root. For