install-openviking-memory · v2026.6.5 · 2026-09-04 · sha256 0c2c081a031aa624

install-openviking-memory v2026.6.5C

Immutable. This exact content is served forever at /api/v1/blob/0c2c081a031aa624.

---
name: install-openviking-memory
description: >
  Install and configure the OpenViking long-term memory plugin for OpenClaw via natural conversation.
  Once installed, the plugin automatically captures facts from chats and recalls relevant context
  before each reply (auto-capture + auto-recall, cross-session). Covers prerequisites, install through
  OpenClaw's plugin manager (ov-install as backup), wizard-based configuration, slot activation,
  gateway restart, verification, multi-tenant root keys, multi-instance and uninstall. The user needs
  no CLI knowledge — the agent runs everything and only asks for a few values. Assumes the OpenViking
  server is already running; it does NOT install the server. Trigger on: "install OpenViking",
  "set up memory", "configure memory plugin", "add long-term memory", "semantic memory", "RAG",
  "帮我装 OpenViking", "配置记忆插件", "安装记忆功能", "接入 OpenViking".
version: 2026.6.5
metadata:
  openclaw:
    requires:
      bins:
        - node
        - openclaw
  emoji: "🦣"
  homepage: "https://github.com/volcengine/OpenViking"
tags:
  - install
  - setup
  - memory
  - openviking
  - rag
  - long-term-memory
---

# Install OpenViking Memory — Agent Installation & Reference Skill

You are about to help a user install and configure the OpenViking memory plugin for OpenClaw.
The user does NOT know how to use a terminal. You will collect a few values through natural conversation, then run **all** commands yourself using your shell tool.

**Read this entire document before starting. Follow every step in order. Do NOT skip steps.**

This skill is split into two halves:

- **Part 1 (STEP 0–10): Natural-language installation walkthrough.** Use this on first install or reconfigure.
- **Part 2: Reference.** Tools, config schema, multi-tenant, multi-instance, daily ops, uninstall, error recovery. Read on demand.

> **Server scope.** This skill **does not** install the OpenViking server itself. It assumes the server is already running locally on `127.0.0.1:1933` or on another machine. If the user has no server, see "Server not ready" handling in STEP 5.

---

# Part 1 — Natural-Language Installation Walkthrough

## STEP 0: Detect Language

If the user's first message contains Chinese characters, respond in **Chinese** throughout.
Otherwise respond in **English**.
All user-facing messages below have (CN)/(EN) variants — use the matching one.

Do NOT show this step to the user.

---

## STEP 1: Detect Operating System (silently)

Run this command and remember the result. Do NOT show it to the user.

```bash
uname -s 2>/dev/null || echo WINDOWS
```

- Output contains `Darwin` → `OS=mac`
- Output contains `Linux` → `OS=linux`
- Output is `WINDOWS` or the command fails → `OS=windows`

---

## STEP 2: Check Prerequisites (silently)

Run silently:

```bash
node -v
openclaw --version
```

**If `node` is missing:**

> (CN) 你的系统没有安装 Node.js。OpenClaw 和 OpenViking 插件需要 Node.js >= 22。请先安装 Node.js,然后再回来找我。
> (EN) Node.js is not installed. OpenClaw and the OpenViking plugin require Node.js >= 22. Please install Node.js first, then come back.

**Stop. Do NOT continue.**

**If `openclaw` is missing:**

> (CN) 你的系统没有安装 OpenClaw。请先安装 OpenClaw(>= 2026.5.27),然后再回来找我。
> (EN) OpenClaw is not installed. Please install OpenClaw (>= 2026.5.27) first, then come back.

**Stop. Do NOT continue.**

If both exist, proceed to STEP 3 silently.

---

## STEP 3: Greet and Ask for 3 Values

Send this message:

> (CN) 好,我来帮你接入 OpenViking 长期记忆。装好之后,我会自动记住对话里的重要信息,下次聊也能回忆起来。
>
> 我需要 3 条信息,不知道的可以问你的管理员:
> 1. **OpenViking 服务地址** —— 例如 `https://ov.example.com` 或 `http://192.168.1.100:1933`,本机服务可以直接说"本机"
> 2. **API Key** —— 用来鉴权;服务没开认证可以说"没有"
> 3. **记忆归属**(可选) —— 所有对话共用 user 记忆(默认),还是在 `peers/<assistant_id>` 或 `peers/<sender_id>` 下分开存放
>
> 先告诉我服务地址吧?

> (EN) I'll set up OpenViking long-term memory for you. Once configured, I'll automatically remember important info from our chats and recall it later.
>
> I need 3 things (ask your admin if unsure):
> 1. **OpenViking server URL** — e.g. `https://ov.example.com` or `http://192.168.1.100:1933`. For a local server, just say "local".
> 2. **API Key** — for auth. Say "none" if the server has no auth.
> 3. **Memory scope** (optional) — shared user memory (default), or peer memory under `peers/<assistant_id>` / `peers/<sender_id>`.
>
> What's the server URL?

---

## STEP 4: Collect Values

Collect 3 values through natural conversation. Be flexible: if the user gives several at once, parse them all. If they correct something, accept the new value.

### 4a. `BASE_URL` (REQUIRED)

- "local" / "本机" / "localhost" → use `http://127.0.0.1:1933`.
- `ov.example.com` without protocol → prepend `https://`.
- Strip trailing `/`, `/health`, or `/api`.
- After normalization must start with `http://` or `https://`.
- If the user says they don't know, ask them to check with the admin or look at how the server was started. **Do NOT make up a URL.**

### 4b. `API_KEY` (OPTIONAL)

> (CN) API Key 是什么?服务没开认证就直接说"没有"。
> (EN) What's the API Key? Say "none" if the server has no auth.

- "no" / "none" / "没有" / "空" / empty → `API_KEY=""` (you will skip the flag later).
- Otherwise store as-is.

### 4c. `PEER_ROLE` (OPTIONAL)

> (CN) 记忆要存在哪一层?默认共享在 `viking://user/<user_id>/memories`(`none`)。也可以按 OpenClaw 助手存到 `.../peers/<assistant_id>/memories`(`assistant`),或按发送者存到 `.../peers/<sender_id>/memories`(`sender`)。
> (EN) Where should memory live? The default is shared `viking://user/<user_id>/memories` (`none`). You can instead use `.../peers/<assistant_id>/memories` (`assistant`) or `.../peers/<sender_id>/memories` (`sender`).

Default to `none`. **Never silently pick another value** — the three options mean different storage layouts and are awkward to change later:

| Value | Concrete layout | Choose it when |
|---|---|---|
| `none` (default) | Shared memory under `viking://user/<user_id>/memories/...`; no concrete `peers/<peer_id>/memories` subtree is used. | General case. All conversations for this OpenViking user share user-level memory. |
| `assistant` | Assistant-attributed peer memory under `viking://user/<user_id>/peers/<assistant_id>/memories/...`, keyed by the resolved OpenClaw agent id (fallback `main`; optional prefix supported). | **Human as OpenViking user**: one human uses multiple assistants and wants their peer memories separated. Example: Alice's `main` and `research` assistants use `.../peers/main/...` and `.../peers/research/...`. |
| `sender` | Sender-attributed peer memory under `viking://user/<user_id>/peers/<sender_id>/memories/...`, keyed by the sender identity supplied by OpenClaw. | **Agent as OpenViking user**: one agent talks to multiple humans and wants their peer memories separated. Example: `support-agent` stores `customer-42` and `customer-99` under different peer subtrees. |

`person` is a legacy alias for `sender`. Accept it from an existing config or explicit user input, but normalize/write new setup values as `sender`.

OpenViking initializes the managed `peers/` container for every user. Do not tell the user that `none` removes that container: it only avoids a concrete peer memory subtree. With `assistant` or `sender`, actor-peer recall includes shared user memory plus the current peer memory; it hides other peer subtrees. Changing this setting does not move existing memories.

- Empty / "default" / "默认" / "共用" → leave unset (plugin defaults to `none`).
- Otherwise pass `--peer-role none|assistant|sender`. Treat explicit legacy `person` as `sender`. Any other value → ask again.

### 4d. `PEER_PREFIX` (OPTIONAL, only when `PEER_ROLE=assistant`)

> (CN) 想给这个 agent 一个记忆前缀吗?留空就用默认。只能用字母、数字、`_`、`-`。
> (EN) Want to set an peer prefix? Leave blank for the default. Letters, digits, `_`, `-` only.

- Only meaningful when `PEER_ROLE=assistant`; skip the question otherwise.
- Empty / "default" / "默认" → leave unset (plugin defaults to `""`).
- Otherwise validate against `/^[A-Za-z0-9_-]+$/`. If invalid, ask again.

### 4e. (Conditional) Multi-Tenant Root-Key Fields

Only ask for these if STEP 7 detects a root key (`Root API key detected. Missing: --account-id, --user-id`). Don't ask up front.

- `ACCOUNT_ID`
- `USER_ID`

See **Reference: Multi-Tenant** for what these mean.

---

## STEP 5: Pre-flight Connectivity Check (silently)

Tell the user briefly:

> (CN) 我先测一下能不能连上服务……
> (EN) Let me test the connection to your server...

Run:

**If OS=windows:**

```powershell
try { (Invoke-WebRequest -Uri "BASE_URL/health" -TimeoutSec 10 -UseBasicParsing -ErrorAction Stop).StatusCode } catch { $_.Exception.Response.StatusCode.value__ }
```

**If OS=mac or OS=linux:**

```bash
curl -sS -o /dev/null -w "%{http_code}" --connect-timeout 10 "BASE_URL/health"
```

Replace `BASE_URL` with the actual value.

| Status | Meaning | Action |
|---|---|---|
| `200` | Server reachable, no auth on `/health` | Proceed to STEP 6. |
| `401` / `403` | Server reachable but `/health` requires auth | Proceed to STEP 6 — the wizard's key probe will sort it out. |
| `000` / timeout / connection refused | Server unreachable | **Server-not-ready handling** below. |
| Anything else | Unexpected | Show status code to the user, go back to STEP 4a. |

### Server-not-ready handling

This skill **does not install or operate the OpenViking server**. If the user's server is unreachable, present the situation honestly and offer two paths:

> (CN) ❌ 我连不上 `BASE_URL`。可能是:
> 1) 服务还没启动 —— 请联系你的 OpenViking 服务管理员把它起起来;如果是你自己负责,请参考 OpenViking 官方文档(`https://github.com/volcengine/OpenViking`)的 server 启动指引。
> 2) 地址不对 —— 你可以重新告诉我正确的地址。
> 3) 网络不通(防火墙 / VPN / 内网)—— 你确认一下网络。
>
> 也可以选择"先把配置写下来",等服务起来就自动生效,要这么办吗?

> (EN) ❌ Cannot reach `BASE_URL`. Likely cause:
> 1) **Server isn't running** — please ask your OpenViking admin to start it. If you own the server, follow the OpenViking official docs (`https://github.com/volcengine/OpenViking`) to start it. **This skill does not install or run the server.**
> 2) **Wrong URL** — give me the correct URL.
> 3) **Network blocked** (firewall / VPN / private network) — please verify connectivity.
>
> Or I can save the config now (`--allow-offline`) so it will activate automatically once the server is up. Want me to do that?

If the user fixes the URL → back to STEP 4a.
If the user wants `--allow-offline` → remember `ALLOW_OFFLINE=true` and continue to STEP 6.
If the user gives up / cannot fix → stop here. Do NOT continue with a broken state.

---

## STEP 6: Install the Plugin

The plugin can be installed two ways. **Always try Path A first.** Use Path B only as a backup when Path A fails because ClawHub is unavailable, rate-limited, or authentication blocks anonymous install. For version conflicts, dependency errors, or other non-registry failures, stop and show the user the error instead of switching paths silently.

### Path A — Primary: `openclaw plugins install` (uses ClawHub)

Tell the user:

> (CN) 现在开始装插件……
> (EN) Installing the plugin now...

Run:

```bash
openclaw plugins install clawhub:@openviking/openclaw-plugin
```

Trigger fallback to Path B only if the output contains any of these strings:

- `429`
- `rate limit` / `rate-limited` / `Too Many Requests`
- `not logged in` / `please log in` / `please login` / `unauthorized` / `401` / `403` together with `clawhub`
- `ETIMEDOUT` / `ECONNRESET` on a `clawhub`-related host
- generic message indicating the registry refused an anonymous client

Before falling back, also try the explicit registry prefix once:

```bash
openclaw plugins install clawhub:@openviking/openclaw-plugin
```

If the install **succeeds**, jump to STEP 7.

If both attempts fail with one of the fallback-eligible errors above, go to Path B. If the failure is a version conflict, missing dependency, package validation error, or another non-registry error, stop and show the last 30 lines to the user.

### Path B — Backup: `ov-install` (bypasses ClawHub)

Tell the user:

> (CN) ClawHub 现在好像被限流、不可用,或者当前账号不能安装。我改用备用路径,通过 npm 下载并部署插件包。
> (EN) ClawHub looks rate-limited, unavailable, or blocked for this account. I'll use the backup path and install the plugin package from npm.

Run the installer with `npx` (no global install needed):

```bash
npx -y openclaw-openviking-setup-helper@latest --base-url BASE_URL [--api-key API_KEY] [--peer-role PEER_ROLE] [--peer-prefix PEER_PREFIX] [--account-id ACCOUNT_ID] [--user-id USER_ID]
```

Build the flag list according to what the user gave you:

- Always pass `--base-url BASE_URL`.
- Pass `--api-key API_KEY` only if `API_KEY` is non-empty.
- Pass `--peer-role PEER_ROLE` only if the user chose `assistant` or `sender`; omit it for the `none` default. Normalize legacy `person` to `sender` first.
- Pass `--peer-prefix PEER_PREFIX` only if the user gave one and `PEER_ROLE=assistant`.
- `--account-id` / `--user-id` only if the root-key path requires them.

`ov-install` will, in one shot:
1. Download the `@openviking/openclaw-plugin` package from npm into a temporary staging dir.
2. Copy the package into the OpenClaw `extensions/` dir and install plugin dependencies.
3. Register the plugin in `openclaw.json` (via `openclaw plugins enable` or direct write).
4. Run `openclaw openviking setup --json --base-url … [--api-key …]` for the user.
5. Return a non-zero exit if setup needs explicit `--allow-offline` or `--force-slot` consent.

This means **STEP 7 is effectively done by `ov-install`**. After `ov-install` exits 0, jump straight to **STEP 9** (gateway restart) and **STEP 10** (verify).

If `ov-install` exits non-zero, capture the last 30 lines of its output, show them to the user, and stop. Don't retry blindly.

---

## STEP 7: Configure (only on Path A — Path B did this for you)

Run the setup wizard non-interactively. Build flags from collected values:

```bash
openclaw openviking setup --base-url BASE_URL --json [--api-key API_KEY] [--peer-role PEER_ROLE] [--peer-prefix PEER_PREFIX] [--account-id ACCOUNT_ID] [--user-id USER_ID] [--allow-offline] [--force-slot]
```

Rules:

- `--base-url BASE_URL` is **required** under `--json`. Without it, the wizard prints `--json requires --base-url for non-interactive mode`.
- `--api-key` only if `API_KEY` is non-empty.
- `--peer-role` only if the user chose `assistant` or `sender`; omit it for the `none` default. Normalize legacy `person` to `sender` first.
- `--peer-prefix` only if the user gave one and `--peer-role assistant` is in play. Use **`--peer-prefix`** for assistant peer prefixes; the old ID-style setup flag is no longer supported.
- `--account-id` / `--user-id` only after STEP 7 root-key detection (see below).
- `--allow-offline` only if the user explicitly approved it in STEP 5.
- `--force-slot` **never** in the first attempt. Add only after the user confirms (see slot_blocked handling below).

### Parse the JSON output

The wizard prints a single JSON object:

```json
{
  "success": true | false,
  "action": "configured" | "existing" | "error" | "slot_blocked",
  "config": { "mode": "remote", "baseUrl": "...", "apiKey": "...", "peer_prefix": "...", "accountId": "...", "userId": "..." },
  "health": { "ok": true, "version": "...", "compatibility": "compatible" | "server_too_old" | "server_too_new" | "unknown" },
  "keyProbe": { "keyType": "user_key" | "root_key" | "no_key" | "unknown", "needsAccountId": false, "needsUserId": false, "detail": "..." },
  "slot": { "activated": true, "replaced": false, "previousOwner": "openviking" },
  "error": "..."
}
```

### Decision matrix

| Condition | Action |
|---|---|
| `success: true` and `action: "configured"` or `"existing"` | Done. Proceed to STEP 9. |
| `success: false` and `action: "slot_blocked"` | **Slot conflict — see below.** |
| `success: false` and `error` contains `"Server unreachable"` | Connectivity broke between STEP 5 and STEP 7. Offer `--allow-offline`; if accepted, retry. Otherwise back to STEP 4a. |
| `success: false` and `error` contains `"Root API key detected"` and `"Missing: --account-id, --user-id"` | **Root-key path — see below.** |
| `success: false` and `error` contains `"Invalid API key"` / `keyProbe.keyType: "unknown"` after auth failure detail | API key wrong. Back to STEP 4b. |
| `success: false` and any other `action: "error"` | Show `error` to the user, stop. Do NOT pretend success. |

### Slot conflict (`slot_blocked`)

The error text looks like:

> `contextEngine slot is owned by "<other-plugin>". Config was saved but slot was NOT changed. Use --force-slot to replace.`

**Do NOT silently use `--force-slot`.** Ask the user:

> (CN) 你的 OpenClaw 当前 contextEngine 槽被 `<other-plugin>` 占着。如果用 OpenViking 替换它,`<other-plugin>` 就不再生效。要替换吗?
> (EN) Your `contextEngine` slot is currently owned by `<other-plugin>`. Activating OpenViking will disable it. Replace?

If the user agrees, retry the same setup command with `--force-slot` appended. If they decline, tell them config has been saved but the slot is unchanged, and stop.

### Root-key path

The error text looks like:

> `Root API key detected. Missing: --account-id, --user-id. Provide both to enable multi-tenant scoping.`

Ask the user:

> (CN) 你给的是 root 级 API Key,需要再补两个值才能用:账户 ID(accountId)和用户 ID(userId)。这两个一般是 OpenViking 服务管理员配的,不知道就问他们。
> (EN) Your API key is a root key, which needs two more values: `accountId` and `userId`. Both come from your OpenViking server admin — ask them if unsure.

After collecting, retry the setup command with `--account-id` and `--user-id` appended.

---

## STEP 8: (Reserved — done inside STEP 7 or by `ov-install`)

The setup wizard already wrote `plugins.entries.openviking.config.*` and (if successful) set `plugins.slots.contextEngine = "openviking"`. There is no separate STEP 8 — go to STEP 9.

---

## STEP 9: Restart the Gateway

```bash
openclaw gateway restart
```

If it fails, try once more with:

```bash
openclaw gateway --force
```

If both fail:

> (CN) Gateway 没能自动重启。请你手动跑一下 `openclaw gateway restart`。重启完告诉我,我来验证。
> (EN) Gateway didn't restart cleanly. Please run `openclaw gateway restart` manually, then tell me when it's done so I can verify.

Wait ~3 seconds before STEP 10.

---

## STEP 10: Verify

```bash
openclaw openviking status --json
```

Expected output:

```json
{
  "configured": true,
  "slotActive": true,
  "health": { "ok": true },
  "config": { "baseUrl": "...", "peer_prefix": "..." }
}
```

### Decision

- `configured: true` **and** `slotActive: true` **and** `health.ok: true` → **success.** Send the success message below.
- `configured: true` but `slotActive: false` → if you previously skipped `--force-slot`, ask the user about replacing now and retry STEP 7 with `--force-slot`. Otherwise it's a gateway restart issue — back to STEP 9.
- `configured: true` but `health.ok: false` → server became unreachable. Tell the user; the config will start working as soon as the server is back.
- `configured: false` → setup didn't actually persist. Show full status JSON to the user and stop.

### Success message

> (CN) 🎉 全部搞定!OpenViking 长期记忆已经接好了。
>
> 工作方式:
> - **每一轮**:你的对话会被自动追加到 OpenViking session 里;自动抽取依赖阈值 commit 或 `/compact`
> - **明确要记住时**:如果你说“记住/保存/存一下”某个长期事实,我会用 `memory_store` 立即提交到记忆管线
> - **`/compact` 时**:待提交的 session 消息会在 OpenViking 服务端被抽取成长期记忆
> - **后续会话**:每次回复前我都会自动搜一下相关记忆并带进上下文
>
> 想验证一下吗?可以直接说“记住我的邮箱是 test@example.com”,让我通过 `memory_store` 立即提交;或者先聊几句包含可记忆事实的话,再调一下 `/compact`。之后开新对话问“我的邮箱是?”,应该就能召回了。

> (EN) 🎉 All set! OpenViking long-term memory is connected.
>
> How it works:
> - **Every turn**: our messages are appended to an OpenViking session; automatic extraction depends on a threshold commit or `/compact`
> - **Explicit remember requests**: if you ask me to remember/save/store a long-term fact, I can use `memory_store` to commit it immediately
> - **On `/compact`**: pending session messages are extracted into long-term memories on the OpenViking server
> - **Future sessions**: relevant memories are auto-retrieved and injected before my replies
>
> Want to verify? Say "remember my email is test@example.com" so I can commit it via `memory_store`, or tell me a few memory-worthy facts and run `/compact`. Then start a new chat and ask "what's my email?" — it should recall.

---

# Part 2 — Reference

## How It Works

The context-engine pipeline has three distinct stages plus one explicit write path — keep them apart, especially when telling users when memories become searchable long-term memory on the OpenViking server:

- **Archive / capture (context-engine `afterTurn`)**: at the end of a user turn, the plugin appends user/assistant messages to the OpenViking session via `POST /api/v1/sessions/.../messages`. This is **session capture only** unless `pending_tokens` crosses `commitTokenThreshold`; below the threshold, no memory extraction runs yet. You'll see session message counts grow on the server, but no new files under `viking://user/.../memories/`.
- **Memory extraction (threshold commit or `/compact`)**: memory extraction runs after a session commit. The commit can be triggered asynchronously when `afterTurn` crosses `commitTokenThreshold`, synchronously when the user invokes OpenClaw's `/compact` command, or explicitly by `memory_store`. The server-side extraction pipeline reads the archived session and writes new memories.
  - `captureMode: "semantic"` (default): server extraction pipeline filters all qualifying text.
  - `captureMode: "keyword"`: only text matching trigger words (e.g. "remember", "preference") is considered.
- **Auto-Recall (context-engine `assemble()`)**: before prompt context is assembled, the plugin queries OpenViking for relevant memories and injects them into context. Recall works even when there are no extracted memories yet — you just won't see anything come back.

**Practical implication for testing**: if you write down a short fact and immediately try to recall it without a threshold commit, `/compact`, or `memory_store`, the plugin may only retrieve it as recent session context, not as a long-term memory. To verify long-term memory cross-session deterministically, run `/compact` or use `memory_store` for the fact being tested.

### Explicit long-term memory writes

Auto-capture is best-effort and commit-dependent. When the user explicitly says to remember, save, or store an important long-term fact, preference, project, or decision, the agent should call `memory_store` instead of waiting for ordinary auto-capture.

Use `memory_store` as the integration-side reliable path for durable-memory intent:

- It writes the supplied text into an OpenViking session and calls `commit(wait=true)`.
- It complements auto-capture; it does not replace normal session capture.
- If it commits but extracts 0 memories, the explicit path has done its job. Treat that as a server-side extraction/model/configuration issue and check OpenViking logs.

## Available Tools

These are the plugin tools the agent can call once installed.

### `memory_recall` — Search Memories

| Parameter | Required | Description |
|---|---|---|
| `query` | Yes | Search query text |
| `limit` | No | Maximum number of results (defaults to plugin config) |
| `scoreThreshold` | No | Minimum relevance score 0–1 (defaults to plugin config) |
| `targetUri` | No | Exact search scope URI. If provided, only that URI is searched. |
| `resourceTypes` | No | Array of target types used when `targetUri` is omitted: `resource`, `user`, `agent`. Defaults to plugin `recallTargetTypes`. |

Example: user asks "What programming language did I say I like?"

### `memory_store` — Manual Store

| Parameter | Required | Description |
|---|---|---|
| `text` | Yes | Information text to store |
| `role` | No | Session role (default `user`) |
| `sessionId` | No | Existing OpenViking session ID |

Use this when the user explicitly asks to remember/save/store a long-term fact, preference, project, or decision.

Example: user says "Remember my email is xxx@example.com".

### `memory_forget` — Delete Memories

| Parameter | Required | Description |
|---|---|---|
| `uri` | No | Exact memory URI (direct delete) |
| `query` | No | Search query (find then delete) |
| `targetUri` | No | Search scope URI |
| `limit` | No | Search limit (default 5) |
| `scoreThreshold` | No | Minimum relevance score |

Example: user says "Forget my phone number".

### `ov_archive_search` — Search Archived Original Messages

| Parameter | Required | Description |
|---|---|---|
| `query` | Yes | A single keyword or short phrase. Prefer concrete names, dates, commands, paths, or distinctive nouns. |
| `archiveId` | No | Optional archive ID such as `archive_002`. |

Use when `[Session History Summary]` is too coarse. Try at least two keyword variants before concluding the detail is unavailable.

### `ov_archive_expand` — Expand An Archive

| Parameter | Required | Description |
|---|---|---|
| `archiveId` | Yes | Archive ID from `[Archive Index]`, e.g. `archive_002`. |

### `add_resource` — Import Resource

The agent-visible `add_resource` tool is disabled by default (`enableAddResourceTool=false`). Use manual `/add-resource` for resource ingestion unless you explicitly opt in. Even when enabled, never use `add_resource` during search, retrieval, URI reading, or search-result optimization; use `ov_search` and `ov_read` for those flows.

| Parameter | Required | Description |
|---|---|---|
| `source` | Yes | Local path, OpenClaw media attachment path, directory path, public URL, or Git URL. |
| `to` | No | Exact target URI under `viking://resources`; mutually exclusive with `parent`. |
| `parent` | No | Parent URI under `viking://resources`; mutually exclusive with `to`. |
| `reason` | No | Reason or note for import. |
| `instruction` | No | Processing instruction for semantic extraction. |
| `wait` | No | Wait for processing completion. |
| `timeout` | No | Timeout in seconds when `wait=true`. |

### `add_skill` — Import Agent Skill

| Parameter | Required | Description |
|---|---|---|
| `source` | No | Local `SKILL.md` path or skill directory. Exactly one of `source` or `data` is required. |
| `data` | No | Raw `SKILL.md` content or MCP tool dict. Exactly one of `source` or `data` is required. |
| `wait` | No | Wait for processing completion. |
| `timeout` | No | Timeout in seconds when `wait=true`. |

Agent Skill best practice: verify frontmatter has a precise `name`, a trigger-oriented `description`, useful `tags`, explicit scope boundaries, and concrete execution steps. Do not include secrets in skill content.

### `ov_search` — Search Resources and Skills

| Parameter | Required | Description |
|---|---|---|
| `query` | Yes | Search query. |
| `uri` | No | Optional search URI. Defaults to resources plus agent skills. |
| `limit` | No | Max results per search scope. Default: 10. |

Important: `ov_search` result URIs are OpenViking virtual URIs, not local file paths. If full content is needed, call `ov_read` with the exact `viking://...` URI returned by search or trace results; do not use filesystem read tools.

### `ov_read` — Read OpenViking Content

| Parameter | Required | Description |
|---|---|---|
| `uri` | Yes | Exact `viking://...` URI returned by `ov_search` or recall trace results. Local file paths and `openviking://...` display aliases are refused. |

### `ov_recall_trace` — Query Recall Trace

| Parameter | Required | Description |
|---|---|---|
| `turn` | No | `latest` or `all`; default `latest`. |
| `traceId` | No | Exact trace ID. |
| `sessionId` / `sessionKey` / `ovSessionId` | No | Session filters. |
| `source` | No | `auto_recall`, `memory_recall`, `ov_search`, or `ov_archive_search`. |
| `resourceTypes` | No | Target type filters: `resource`, `user`, `agent`. |
| `since` / `until` | No | Unix timestamp bounds in milliseconds. |
| `includeContent` | No | Read selected/displayed URI content previews on demand. |
| `limit` | No | Maximum traces to return. Default: 20. |

Requires `traceRecall=true`; persisted lookup also requires `traceRecallPersist=true`.

### Externalized Tool Result Tools

Use these when a preview contains a `viking://session/<session_id>/tool-results/<tool_result_id>` ref.

| Tool | Parameters |
|---|---|
| `openviking_tool_result_list` | `tool_name?`, `limit?` (default 50) |
| `openviking_tool_result_search` | `tool_output_ref`, `query`, `limit?` (default 20), `context_chars?` (default 300) |
| `openviking_tool_result_read` | `tool_output_ref`, `offset?` (default 0), `limit?` (default 20000) |

## Configuration Schema

These are the keys under `plugins.entries.openviking.config` in `openclaw.json`. The setup wizard / `ov-install` sets the first few; the rest are tunables.

| Field | Default | Description |
|---|---|---|
| `mode` | `"remote"` (forced by plugin) | Always remote in this skill. Don't set manually. |
| `baseUrl` | `http://127.0.0.1:1933` | OpenViking server URL. |
| `apiKey` | — | API key. Optional if server has no auth. |
| `peer_role` | `none` | Memory scope: `none` (shared `viking://user/<user_id>/memories`), `assistant` (`.../peers/<assistant_id>/memories`), or `sender` (`.../peers/<sender_id>/memories`). Legacy `person` is accepted as `sender`. Session messages use body `peer_id`; data-plane recall/search uses `X-OpenViking-Actor-Peer`. |
| `peer_prefix` | `""` | Optional prefix for assistant `peer_id` / actor peer values. Only used when `peer_role=assistant`. Letters / digits / `_` / `-`. |
| `accountId` | — | Required when `apiKey` is a root key. |
| `userId` | — | Required when `apiKey` is a root key. |
| `targetUri` | `viking://~/memories` | Default search scope URI. |
| `timeoutMs` | (plugin default) | HTTP timeout for OpenViking calls. |
| `autoCapture` | `true` | Auto-append turn messages to the OpenViking session at `afterTurn`; extraction runs only after a threshold commit, `/compact`, or explicit `memory_store`. |
| `captureMode` | `"semantic"` | Filter mode used by the server-side extraction pipeline: `semantic` or `keyword`. |
| `captureMaxLength` | `24000` | Max text length per archived turn. |
| `autoRecall` | `true` | Auto-recall and inject memories before reply. |
| `recallTargetTypes` | `user,agent` | Default target types when `targetUri` is omitted. Allowed: `resource`, `user`, `agent`. |
| `recallResources` | `false` | Compatibility shortcut that appends `resource` to default recall targets when `recallTargetTypes` is unset. |
| `recallLimit` | `6` | Max memories injected per recall. |
| `recallScoreThreshold` | `0.15` | Min relevance score to inject. |
| `recallMaxInjectedChars` | `4000` | Hard cap on injected character count. Complete memories that do not fit are skipped, not truncated. |
| `recallPreferAbstract` | (plugin default) | Prefer abstract memories over raw. |
| `recallTokenBudget` | deprecated | Compatibility alias for `recallMaxInjectedChars`. |
| `commitTokenThresholdRatio` | `0.5` | Async-commit threshold as a fraction (0-1) of the model context window (e.g. 0.5 = 50%); `0` commits every turn. |
| `commitKeepRecentCount` | `10` | Recent messages kept live after afterTurn commit. Compact always uses `0`. |
| `bypassSessionPatterns` | — | Glob patterns for sessions skipped by capture. |
| `ingestReplyAssist` | (plugin default) | Reply-assist ingestion toggle. |
| `emitStandardDiagnostics` | `false` | Emit structured `openviking: diag {...}` lines. |
| `logFindRequests` | `false` | Log routing for find/session writes. Also enabled by `OPENVIKING_LOG_ROUTING=1` or `OPENVIKING_DEBUG=1`. |
| `traceRecall` | `false` | Record recall traces in memory. |
| `traceRecallPersist` | `false` | Persist recall traces to JSONL files. |
| `traceRecallDir` | `~/.openclaw/openviking/recall-traces` | Recall trace directory. |

To change a value:

```bash
openclaw config set plugins.entries.openviking.config.<field> <value>
openclaw gateway restart
```

## Multi-Tenant (Root API Keys)

Some OpenViking deployments use a single **root** API key shared across tenants. In that case the plugin needs both `accountId` and `userId` so it can scope memories correctly. The setup wizard detects this automatically and returns:

```
Root API key detected. Missing: --account-id, --user-id
```

When you see this:

1. Ask the user for both values (they come from the OpenViking admin).
2. Retry STEP 7 with both flags:

```bash
openclaw openviking setup --base-url BASE_URL --api-key API_KEY --account-id ACCOUNT_ID --user-id USER_ID --json
```

A **user key** (issued per tenant) does not need these flags.

## Multi-Instance (`--workdir` / `OPENCLAW_STATE_DIR`)

If the user runs multiple OpenClaw instances (e.g. testing several agents in parallel), each has its own state dir.

To target a non-default instance:

```bash
npx -y openclaw-openviking-setup-helper@latest --workdir ~/.openclaw-second --base-url ... --api-key ...
```

`ov-install` writes a helper env file when the state dir is non-default:

- Unix: `~/.openclaw/openviking.env` containing `export OPENCLAW_STATE_DIR='...'`
- Windows: `~/.openclaw/openviking.env.bat` and `.ps1` setting the same variable

Source it before running `openclaw` commands so they hit the correct state:

**Unix:**
```bash
source ~/.openclaw/openviking.env
openclaw status
```

**Windows (PowerShell):**
```powershell
. "$HOME/.openclaw/openviking.env.ps1"
openclaw status
```

Or pass `--workdir` directly to each `openclaw` invocation (note: not all `openclaw` subcommands honor `--workdir` consistently — when in doubt, prefer the env var).

## Daily Operations

```bash
# Start or restart OpenClaw gateway after config changes
openclaw gateway restart

# Check overall status
openclaw status
openclaw openviking status --json

# Read current OpenViking slot
openclaw config get plugins.slots.contextEngine

# Disable OpenViking memory (keep config, deactivate slot)
openclaw config set plugins.slots.contextEngine legacy
openclaw gateway restart

# Re-enable
openclaw config set plugins.slots.contextEngine openviking
openclaw gateway restart
```

## Uninstall

### Preferred: via OpenClaw plugin manager

```bash
openclaw plugins uninstall openviking
openclaw config set plugins.slots.contextEngine legacy
openclaw gateway restart
```

### If installed through backup `ov-install`

```bash
npx -y openclaw-openviking-setup-helper@latest --uninstall
```

This will:

1. Stop the OpenClaw gateway.
2. Back up `openclaw.json`.
3. Remove all OpenViking plugin config from `openclaw.json` (`plugins.entries.openviking`, `plugins.allow`, `plugins.installs.openviking`, `plugins.slots.contextEngine`).
4. Move the plugin directory to `disabled-extensions/` as backup.
5. Remove the helper env files (`openviking.env`, `.bat`, `.ps1`).

Add `--base-url dummy` for fully non-interactive mode (skips the confirmation prompt).

### Manual

```bash
openclaw gateway stop
openclaw config set plugins.slots.contextEngine legacy
# Then edit ~/.openclaw/openclaw.json and remove openviking from
#   plugins.entries
#   plugins.allow
#   plugins.installs
openclaw gateway restart
```

## Error Recovery Reference

Match against actual stderr / JSON `error` strings.

| Symptom (exact string) | Likely cause | Fix |
|---|---|---|
| `command not found: openclaw` | OpenClaw not installed | Stop. Ask user to install OpenClaw >= 2026.5.27. |
| `command not found: node` / `'node' is not recognized` | Node missing | Stop. Ask user to install Node.js >= 22. |
| `429` / `rate limit` / `Too Many Requests` from `plugins install` | ClawHub throttle | Switch to backup Path B (`ov-install`). |
| `not logged in` / `please log in` from `plugins install` | Anonymous user hit limit | Switch to backup Path B (`ov-install`). |
| `ECONNREFUSED` / `connection refused` on `BASE_URL/health` | Server not running | Server-not-ready handling in STEP 5. |
| `ETIMEDOUT` on `BASE_URL/health` | Network blocked | Ask about firewall / VPN / proxy. |
| `--json requires --base-url for non-interactive mode` | Built command wrong | Add `--base-url BASE_URL` and retry. |
| `Server unreachable: …. Use --allow-offline to save config anyway.` | Setup couldn't reach server | Offer `--allow-offline`. |
| `contextEngine slot is owned by "<x>". … Use --force-slot to replace.` | Slot conflict | Ask user, then retry with `--force-slot`. |
| `Root API key detected. Missing: --account-id, --user-id` | Multi-tenant key | Collect both, retry with `--account-id` `--user-id`. |
| `openviking: config parse failed` (in gateway log) | Bad value in `openclaw.json` | Show user; check `peer_prefix` charset, URL format. |
| `extracted 0 memories` after a turn | Server VLM/embedding misconfigured | **Out of scope.** Tell user this is a server-side issue — ask their OpenViking admin to check VLM / embedding config. |
| `401` / `403` on plugin requests, but `/health` works | Server requires auth on API endpoints | Re-run STEP 7 with the correct `--api-key`. |
| Plugin doesn't appear in `openclaw plugins list` after Path A | Install didn't actually finish | Re-run Path A; use Path B only if the failure is registry/rate-limit related. |

## Important Rules

1. **Never ask the user to run commands.** You run everything via your shell tool.
2. **Never skip STEP 5 (connectivity check).** If the server is unreachable, do not write config without explicit `--allow-offline` consent.
3. **Never silently use `--force-slot`.** Slot replacement disables another plugin — always confirm with the user first.
4. **Never invent values.** If the user can't provide a required value, stop and tell them what to ask their admin.
5. **Never claim success without STEP 10.** Only after `openclaw openviking status --json` shows `configured: true && slotActive: true && health.ok: true` may you tell the user it's done.
6. **Use `--peer-prefix` for assistant peer prefixes.** The old ID-style setup flag is no longer supported by the schema.
7. **For Windows, use PowerShell equivalents.** Don't rely on `nohup`, `&`, `mkdir -p`, `source`, etc.
8. **Switch to Path B (ov-install) only for ClawHub/rate-limit/registry availability failures.** Don't use it to hide version conflicts or package validation errors.
9. **Do NOT install or operate the OpenViking server.** This skill assumes the server is already running. If it isn't, tell the user to contact their admin or follow the OpenViking docs.
10. **Be brief and friendly in user-visible text.** Save technical detail for when something actually fails.
11. **Do NOT use `clawhub install openviking`.** That installs a different thing (an AgentSkill, not the plugin).