{"owner":"volcengine","repo":"OpenViking","hasSkills":true,"totalSkillsCount":16,"totalTokensCount":23122,"categories":["anthropic-skill","plugin-manifest"],"hasMcp":false,"mcpConfig":null,"found":["bot/workspace/skills/github/SKILL.md","bot/workspace/skills/github-proxy/SKILL.md","bot/workspace/skills/opencode/SKILL.md","examples/skills/ov-server-operate/SKILL.md","bot/workspace/skills/summarize/SKILL.md","bot/workspace/skills/cron/SKILL.md","bot/workspace/skills/tmux/SKILL.md","bot/workspace/skills/skill-creator/SKILL.md","bot/workspace/skills/weather/SKILL.md","examples/openclaw-plugin/skills/install-openviking-memory/SKILL.md","examples/opencode/plugin/skills/openviking/SKILL.md","examples/openclaw-plugin/openclaw.plugin.json","bot/workspace/skills/README.md","examples/codex-memory-plugin/.codex-plugin/plugin.json","examples/claude-code-memory-plugin/.claude-plugin/plugin.json","bot/workspace/skills/opencode/status.json"],"skills":{"bot/workspace/skills/github/SKILL.md":"---\nname: github\ndescription: \"Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.\"\nmetadata: {\"vikingbot\":{\"emoji\":\"🐙\",\"requires\":{\"bins\":[\"gh\"]},\"install\":[{\"id\":\"brew\",\"kind\":\"brew\",\"formula\":\"gh\",\"bins\":[\"gh\"],\"label\":\"Install GitHub CLI (brew)\"},{\"id\":\"apt\",\"kind\":\"apt\",\"package\":\"gh\",\"bins\":[\"gh\"],\"label\":\"Install GitHub CLI (apt)\"}]}}\n---\n\n# GitHub Skill\n\nUse the `gh` CLI to interact with GitHub. Always specify `--repo owner/repo` when not in a git directory, or use URLs directly.\n\n## Pull Requests\n\nCheck CI status on a PR:\n```bash\ngh pr checks 55 --repo owner/repo\n```\n\nList recent workflow runs:\n```bash\ngh run list --repo owner/repo --limit 10\n```\n\nView a run and see which steps failed:\n```bash\ngh run view <run-id> --repo owner/repo\n```\n\nView logs for failed steps only:\n```bash\ngh run view <run-id> --repo owner/repo --log-failed\n```\n\n## API for Advanced Queries\n\nThe `gh api` command is useful for accessing data not available through other subcommands.\n\nGet PR with specific fields:\n```bash\ngh api repos/owner/repo/pulls/55 --jq '.title, .state, .user.login'\n```\n\n## JSON Output\n\nMost commands support `--json` for structured output.  You can use `--jq` to filter:\n\n```bash\ngh issue list --repo owner/repo --json number,title --jq '.[] | \"\\(.number): \\(.title)\"'\n```\n","bot/workspace/skills/github-proxy/SKILL.md":"---\nname: github-proxy\ndescription: GitHub 国内访问加速 skill，使用 githubproxy.cc 代理加速 GitHub 仓库克隆、文件下载、Raw 文件访问等操作。使用场景：(1) 需要 git clone GitHub 仓库时加速，(2) 下载 GitHub Release 文件、Raw 文件、Archive 压缩包时加速，(3) 任何需要访问 GitHub 资源但速度慢的场景\n---\n\n# GitHub 国内代理加速 Skill\n\n使用 githubproxy.cc 代理服务，为国内访问 GitHub 提供加速支持。\n\n## 代理服务\n\n当前使用的代理服务：\n- **主要服务**: githubproxy.cc (测试有效，加速约 3 倍)\n- **备用服务**: ghfast.top\n\n## 使用方法\n\n### 1. Git Clone 加速\n\n将 GitHub 仓库链接前加上 `https://githubproxy.cc/` 前缀：\n\n```bash\n# 原始链接\ngit clone https://github.com/username/repo.git\n\n# 加速链接\ngit clone https://githubproxy.cc/https://github.com/username/repo.git\n```\n\n### 2. 文件下载加速\n\n支持以下类型的 GitHub 资源加速：\n\n- **Raw 文件**: `https://raw.githubusercontent.com/...`\n- **Release 文件**: 项目发布的附件\n- **Archive 压缩包**: 仓库打包下载\n- **Gist 文件**: `gist.github.com` 或 `gist.githubusercontent.com`\n\n```bash\n# 原始链接\nwget https://raw.githubusercontent.com/username/repo/main/file.txt\n\n# 加速链接\nwget https://githubproxy.cc/https://raw.githubusercontent.com/username/repo/main/file.txt\n```\n\n### 3. 使用辅助脚本\n\n使用 `scripts/convert_url.py` 自动转换 GitHub 链接：\n\n```bash\npython scripts/convert_url.py \"https://github.com/username/repo.git\"\n```\n\n## 链接转换规则\n\n| 原始链接格式 | 转换后格式 |\n|-------------|-----------|\n| `https://github.com/username/repo.git` | `https://githubproxy.cc/https://github.com/username/repo.git` |\n| `https://raw.githubusercontent.com/...` | `https://githubproxy.cc/https://raw.githubusercontent.com/...` |\n| `https://github.com/.../releases/download/...` | `https://githubproxy.cc/https://github.com/.../releases/download/...` |\n| `https://github.com/.../archive/...` | `https://githubproxy.cc/https://github.com/.../archive/...` |\n\n## 注意事项\n\n- 本服务仅供学习研究使用，请勿滥用\n- 如果 githubproxy.cc 不可用，请尝试备用服务 ghfast.top\n- 不支持 SSH Key 方式的 git clone\n- Push、PR、Issue 等操作建议直接使用官方 GitHub 地址\n","bot/workspace/skills/opencode/SKILL.md":"---\nname: opencode\ndescription: Monitor and manage your OpenCode tasks using helper scripts.\nmetadata: {\"vikingbot\":{\"emoji\":\"💻\",\"requires\":{\"bins\":[\"python3\"]}}}\n---\n\n# OpenCode Skill\n\nUse helper scripts to manage your OpenCode instances.\n\n## Helper Scripts\n\nAll scripts are in the workspace skills directory. Run them with:\n```bash\nuv run python skills/opencode/script_name.py\n```\n\nNote: Do not kill the opencode process.\n\n## Scripts\n\n### `list_sessions.py` \nListing all OpenCode sessions\n\nExample:\nuv run python skills/opencode/list_sessions.py\n\n\n### `list_messages_of_session.py`\nListing latest OpenCode messages by session_id \n\nExample:\nuv run python skills/opencode/list_sessions.py {session_id}\n\n## Session Status Types\n\n- **🟢 WAITING**: Last message was from user - agent is waiting for input\n- **🔴 WORKING**: Last message was from assistant - agent recently finished or may be working\n- **🟡 UNKNOWN**: Cannot determine status\n\n","examples/skills/ov-server-operate/SKILL.md":"---\nname: ov-server-operate\ndescription: Operate and maintain OpenViking server - configure, install, start, stop, and cleanup the server. Use when need to setup or manage OpenViking service deployment.\ncompatibility: OpenViking CLI configured at `~/.openviking/ovcli.conf`\n---\n\n# OpenViking Server Operations\n\nThis guide provides standard operating procedures for deploying, managing, and maintaining OpenViking servers in production environments.\n\n## Table of Content\n- Service Configuration\n- Environment Setup with uv\n- Server Startup with nohup\n- Server Shutdown\n- Data Cleanup Procedure\n- Verification and Troubleshooting\n\n## Service Configuration\n\n### Default Paths and Structure\n\nOpenViking uses the following standard directory structure under `~/.openviking/`:\n\n```\n~/.openviking/\n├── ov.conf             # Server configuration (required)\n├── ovcli.conf          # CLI client configuration\n├── ov-venv/            # Virtual environment (created by uv)\n├── log/                # Server log directory\n│   ├── openviking-server.log   # server stdout log\n│   └── openviking.log          # server log\n└── data/               # Workspace data (configured in ov.conf)\n    ├── ...\n    └── ...\n```\n\n### Configuration Files\n\n#### 1. Server Config (`~/.openviking/ov.conf`)\n\nCreate the configuration file with at minimum the following configuration.\nNote 1: Replace the api-key with your own api-key. If you don't have one, ask the user to get one (follow the Volcengine Ark platform guide).\nNote 2: Replace the root_api_key with your own root-api-key. Ask the user to set one — it will be used for authentication when the CLI connects to the server.\n\n```json\n{\n  \"server\": {\n    \"host\": \"0.0.0.0\",\n    \"port\": 1933,\n    \"root_api_key\": \"your-root-api-key\"\n  },\n  \"storage\": {\n    \"workspace\": \"~/.openviking/data/\"\n  },\n  \"parsers\": {\n    \"code\": {\n      \"gitlab_domains\": [\"code.byted.org\"],\n      \"azure_devops_domains\": [\"ssh.dev.azure.com\", \"vs-ssh.visualstudio.com\"]\n    }\n  },\n  \"embedding\": {\n    \"dense\": {\n        \"model\": \"doubao-embedding-vision-251215\",\n        \"api_key\": \"your-volcengine-api-key\",\n        \"api_base\": \"https://ark.cn-beijing.volces.com/api/v3\",\n        \"dimension\": 1024,\n        \"input\": \"multimodal\",\n        \"provider\": \"volcengine\"\n    }\n  },\n  \"vlm\": {\n    \"model\": \"doubao-seed-1-8-251228\",\n    \"api_key\": \"your-volcengine-api-key\",\n    \"api_base\": \"https://ark.cn-beijing.volces.com/api/v3\",\n    \"temperature\": 0.0,\n    \"max_retries\": 2,\n    \"provider\": \"volcengine\",\n    \"thinking\": false\n  },\n  \"log\": {\n    \"level\": \"INFO\",\n    \"output\": \"file\",\n    \"rotation\": true,\n    \"rotation_days\": 3,\n    \"rotation_interval\": \"midnight\"\n  }\n}\n```\n\n#### 2. CLI Config (`~/.openviking/ovcli.conf`)\n\nFor client connections from localhost:\n\n```json\n{\n  \"url\": \"http://localhost:1933\",\n  \"api_key\": \"your-root-api-key\"\n}\n```\n\nFor remote connections, set the url to the remote server address (for example, the server EIP).\n\n## Environment Setup with uv\n\n### Step 1: Install uv (if not already installed)\n\n```bash\n# macOS/Linux\ncurl -LsSf https://astral.sh/uv/install.sh | sh\n\n# Verify installation\nuv --version\n```\n\n### Step 2: Create Virtual Environment\n\nCreate a dedicated virtual environment at `~/.openviking/ov-venv`:\n\n```bash\n# Create venv with Python 3.10+\ncd ~/.openviking\nuv venv --python 3.12 ov-venv\n```\n\n### Step 3: Activate and Install OpenViking\n\n```bash\n# Activate the virtual environment\nsource ~/.openviking/ov-venv/bin/activate\n\n# Install or upgrade to latest openviking\nuv pip install --upgrade openviking --force-reinstall\n\n# Verify installation\nwhich openviking-server\nopenviking-server --version\nopenviking-server --help\n```\n\n### Step 4: Create Log Directory\n\n```bash\nmkdir -p ~/.openviking/log\n```\n\n## Server Startup with nohup\n\n### Standard Startup Procedure\n\n```bash\n# 1. Activate the virtual environment\nsource ~/.openviking/ov-venv/bin/activate\n\n# 2. Ensure log directory exists\nmkdir -p ~/.openviking/log\n\n# 3. Start server with nohup\nnohup openviking-server \\\n    > ~/.openviking/log/openviking-server.log 2>&1 &\n\n# 4. Save PID for later reference\necho $! > ~/.openviking/server.pid\n\n# 5. Verify startup after 10 secs\nsleep 10\ncurl -s http://localhost:1933/health\n```\n\n### Verify Server is Running\n\n```bash\n# Method 1: Check health endpoint\ncurl http://localhost:1933/health\n# Expected: {\"status\": \"ok\"}\n\n# Method 2: Check readiness (includes storage checks)\ncurl http://localhost:1933/ready\n\n# Method 3: Check process\nps aux | grep openviking-server | grep -v grep\n\n# Method 4: Check log output\ntail -10 ~/.openviking/log/openviking-server.log\ntail -50 ~/.openviking/log/openviking.log\n```\n\n## Server Shutdown\n\n### Graceful Shutdown Procedure\n\n```bash\n# 1. Find the server process\nps aux | grep openviking-server | grep -v grep\n\n# 2. Send SIGTERM for graceful shutdown\n# Option A: Using saved PID\nif [ -f ~/.openviking/server.pid ]; then\n    kill $(cat ~/.openviking/server.pid)\n    rm ~/.openviking/server.pid\nfi\n\n# Option B: Using pgrep\npkill -f openviking-server\n\n# 3. Wait for process to stop\nsleep 3\n\n# 4. Verify it stopped\nps aux | grep openviking-server | grep -v grep || echo \"Server stopped successfully\"\n\n# 5. If still running, force kill\nif pgrep -f openviking-server > /dev/null; then\n    echo \"Force killing server...\"\n    pkill -9 -f openviking-server\nfi\n```\n\n## Data Cleanup Procedure\n\n### When to Use This Procedure\n\nPerform full data cleanup in these scenarios:\n1. Version upgrade with incompatible data format\n2. Corrupted or inconsistent data\n3. Need to reset to fresh state\n4. Storage space reclamation\n\n### Standard Cleanup Workflow\n\n**CRITICAL: ALWAYS BACKUP BEFORE DELETING DATA**\n\n```bash\n# ==========================================\n# STEP 1: STOP THE SERVER FIRST\n# ==========================================\necho \"Step 1: Stopping OpenViking Server...\"\nif pgrep -f openviking-server > /dev/null; then\n    pkill -f openviking-server\n    sleep 3\n    if pgrep -f openviking-server > /dev/null; then\n        pkill -9 -f openviking-server\n        sleep 1\n    fi\nfi\n\n# Verify server is stopped\nif pgrep -f openviking-server > /dev/null; then\n    echo \"ERROR: Server still running! Cannot proceed.\"\n    exit 1\nfi\necho \"✓ Server stopped\"\n\n# ==========================================\n# STEP 2: CREATE BACKUP (REQUIRED)\n# ==========================================\necho \"\"\necho \"Step 2: Creating backup...\"\nBACKUP_DATE=$(date +%Y%m%d_%H%M%S)\nBACKUP_DIR=~/.openviking/backup_${BACKUP_DATE}\n\nmkdir -p ${BACKUP_DIR}\n\n# Backup config files\ncp ~/.openviking/ov.conf ${BACKUP_DIR}/ 2>/dev/null || true\ncp ~/.openviking/ovcli.conf ${BACKUP_DIR}/ 2>/dev/null || true\n\n# Backup workspace (if exists)\nWORKSPACE=$(python3 -c '\nimport json\nimport os\nconfig_path = os.path.expanduser(\"~/.openviking/ov.conf\")\nif os.path.exists(config_path):\n    with open(config_path) as f:\n        cfg = json.load(f)\n        ws = cfg.get(\"storage\", {}).get(\"workspace\", \"./data\")\n        print(os.path.expanduser(ws))\n' 2>/dev/null || echo \"~/.openviking/data\")\n\nif [ -d \"${WORKSPACE}\" ]; then\n    echo \"Backing up workspace: ${WORKSPACE}\"\n    tar -czf ${BACKUP_DIR}/workspace_backup.tar.gz -C $(dirname ${WORKSPACE}) $(basename ${WORKSPACE})\nfi\n\n# Backup log\nif [ -d ~/.openviking/log ]; then\n    cp -r ~/.openviking/log ${BACKUP_DIR}/ 2>/dev/null || true\nfi\n\necho \"✓ Backup created at: ${BACKUP_DIR}\"\nls -lh ${BACKUP_DIR}/\n\n# ==========================================\n# STEP 3: CONFIRM DELETION\n# ==========================================\necho \"\"\necho \"==========================================\"\necho \"WARNING: ABOUT TO DELETE ALL DATA!\"\necho \"==========================================\"\necho \"Workspace to delete: ${WORKSPACE}\"\necho \"Backup location: ${BACKUP_DIR}\"\necho \"\"\nread -p \"Type 'DELETE' to confirm data removal: \" CONFIRM\n\nif [ \"${CONFIRM}\" != \"DELETE\" ]; then\n    echo \"Cleanup cancelled. Backup preserved at ${BACKUP_DIR}\"\n    exit 0\nfi\n\n# ==========================================\n# STEP 4: DELETE DATA\n# ==========================================\necho \"\"\necho \"Step 4: Deleting data...\"\n\n# Delete workspace\nif [ -d \"${WORKSPACE}\" ]; then\n    echo \"Deleting workspace: ${WORKSPACE}\"\n    rm -rf \"${WORKSPACE}\"\nfi\n\n# Optional: Delete old log (uncomment if needed)\n# echo \"Deleting old log...\"\n# rm -rf ~/.openviking/log/*\n\n# Cleanup any temporary files\nrm -f ~/.openviking/server.pid\n\necho \"✓ Data deleted successfully\"\n\n# ==========================================\n# STEP 5: COMPLETION\n# ==========================================\necho \"\"\necho \"==========================================\"\necho \"Cleanup Complete!\"\necho \"==========================================\"\necho \"Backup preserved at: ${BACKUP_DIR}\"\necho \"\"\necho \"Next steps:\"\necho \"1. Reconfigure ov.conf if needed\"\necho \"2. Start the server: openviking-server\"\necho \"3. Verify with: curl http://localhost:1933/health\"\necho \"\"\necho \"To restore from backup:\"\necho \"  tar -xzf ${BACKUP_DIR}/workspace_backup.tar.gz -C $(dirname ${WORKSPACE})\"\n```\n\n### Quick Cleanup (for Development Only)\n\n```bash\n# WARNING: Only use in development!\n# No backup created - data loss guaranteed!\n\n# 1. Stop server\npkill -f openviking-server\nsleep 2\npkill -9 -f openviking-server 2>/dev/null || true\n\n# 2. Delete workspace (adjust path as needed)\nrm -rf ~/.openviking/data\n\n# 3. Cleanup PID and temp files\nrm -f ~/.openviking/server.pid\n\necho \"Quick cleanup complete\"\n```\n\n## Verification and Troubleshooting\n\n### Health Check Verification\n\n```bash\n# Basic health check (always available)\ncurl http://localhost:1933/health\n# Expected: {\"status\": \"ok\"}\n\n# Readiness check (verifies all components)\ncurl http://localhost:1933/ready\n# Expected: {\"status\": \"ready\", \"checks\": {\"agfs\": \"ok\", \"vectordb\": \"ok\", \"api_key_manager\": \"ok\"}}\n\n# System status via CLI (~/.openviking/ovcli.conf should be configured)\nov status\n```\n\n### Common Issues and Solutions\n\n#### Issue: Server won't start\n\n**Check:**\n```bash\n# 1. Check if port is in use\nlsof -i :1933\nnetstat -tulpn | grep 1933\n\n# 2. Check log for errors\ntail -10 ~/.openviking/log/openviking-server.log\ntail -100 ~/.openviking/log/openviking.log\n\n\n# 3. Verify config file is valid JSON\npython3 -c 'import json, os; json.load(open(os.path.expanduser(\"~/.openviking/ov.conf\"))); print(\"Config is valid\")'\n\n# 4. Verify virtual environment\nsource ~/.openviking/ov-venv/bin/activate\nwhich openviking-server\npip list | grep openviking\n```\n\n**Solution:**\n```bash\n# If port conflict: kill the process or use different port\nlsof -ti :1933 | xargs kill -9 2>/dev/null || true\n\n# Or start on different port\nnohup openviking-server --port 1934 > ~/.openviking/log/openviking-server.log 2>&1 &\n```\n\n#### Issue: API Key Errors\n\n**Check:**\n```bash\n# Verify API keys in config\npython3 -c '\nimport json, os\ncfg = json.load(open(os.path.expanduser(\"~/.openviking/ov.conf\")))\nprint(\"Embedding provider:\", cfg.get(\"embedding\", {}).get(\"dense\", {}).get(\"provider\"))\nprint(\"VLM provider:\", cfg.get(\"vlm\", {}).get(\"provider\"))\nprint(\"API keys set:\", bool(cfg.get(\"embedding\", {}).get(\"dense\", {}).get(\"api_key\")), bool(cfg.get(\"vlm\", {}).get(\"api_key\")))\n'\n```\n\n**Solution:** Verify API keys are correct and have the required permissions. Check network connectivity to the model provider endpoints. Ensure API keys are not expired.\n\n## Prerequisites\n\n- Python 3.10+ installed\n- uv package manager available\n- Sufficient disk space for workspace and log\n- API keys for embedding and VLM models configured\n- Network access to model providers (if using cloud models)\n","bot/workspace/skills/summarize/SKILL.md":"---\nname: summarize\ndescription: Summarize or extract text/transcripts from URLs, podcasts, and local files (great fallback for “transcribe this YouTube/video”).\nhomepage: https://summarize.sh\nmetadata: {\"vikingbot\":{\"emoji\":\"🧾\",\"requires\":{\"bins\":[\"summarize\"]},\"install\":[{\"id\":\"brew\",\"kind\":\"brew\",\"formula\":\"steipete/tap/summarize\",\"bins\":[\"summarize\"],\"label\":\"Install summarize (brew)\"}]}}\n---\n\n# Summarize\n\nFast CLI to summarize URLs, local files, and YouTube links.\n\n## When to use (trigger phrases)\n\nUse this skill immediately when the user asks any of:\n- “use summarize.sh”\n- “what’s this link/video about?”\n- “summarize this URL/article”\n- “transcribe this YouTube/video” (best-effort transcript extraction; no `yt-dlp` needed)\n\n## Quick start\n\n```bash\nsummarize \"https://example.com\" --model google/gemini-3-flash-preview\nsummarize \"/path/to/file.pdf\" --model google/gemini-3-flash-preview\nsummarize \"https://youtu.be/dQw4w9WgXcQ\" --youtube auto\n```\n\n## YouTube: summary vs transcript\n\nBest-effort transcript (URLs only):\n\n```bash\nsummarize \"https://youtu.be/dQw4w9WgXcQ\" --youtube auto --extract-only\n```\n\nIf the user asked for a transcript but it’s huge, return a tight summary first, then ask which section/time range to expand.\n\n## Model + keys\n\nSet the API key for your chosen provider:\n- OpenAI: `OPENAI_API_KEY`\n- Anthropic: `ANTHROPIC_API_KEY`\n- xAI: `XAI_API_KEY`\n- Google: `GEMINI_API_KEY` (aliases: `GOOGLE_GENERATIVE_AI_API_KEY`, `GOOGLE_API_KEY`)\n\nDefault model is `google/gemini-3-flash-preview` if none is set.\n\n## Useful flags\n\n- `--length short|medium|long|xl|xxl|<chars>`\n- `--max-output-tokens <count>`\n- `--extract-only` (URLs only)\n- `--json` (machine readable)\n- `--firecrawl auto|off|always` (fallback extraction)\n- `--youtube auto` (Apify fallback if `APIFY_API_TOKEN` set)\n\n## Config\n\nOptional config file: `~/.summarize/config.json`\n\n```json\n{ \"model\": \"openai/gpt-5.2\" }\n```\n\nOptional services:\n- `FIRECRAWL_API_KEY` for blocked sites\n- `APIFY_API_TOKEN` for YouTube fallback\n","bot/workspace/skills/cron/SKILL.md":"---\nname: cron\ndescription: Schedule reminders and recurring tasks.\n---\n\n# Cron\n\nUse the `cron` tool to schedule reminders or recurring tasks.\n\n## Three Modes\n\n1. **Reminder** - message is sent directly to user\n2. **Task** - message is a task description, agent executes and sends result\n3. **One-time** - runs once at a specific time, then auto-deletes\n\n## Examples\n\nFixed reminder:\n```\ncron(action=\"add\", message=\"Time to take a break!\", every_seconds=1200)\n```\n\nDynamic task (agent executes each time):\n```\ncron(action=\"add\", message=\"Check HKUDS/vikingbot GitHub stars and report\", every_seconds=600)\n```\n\nOne-time scheduled task (compute ISO datetime from current time):\n```\ncron(action=\"add\", message=\"Remind me about the meeting\", at=\"<ISO datetime>\")\n```\n\nList/remove:\n```\ncron(action=\"list\")\ncron(action=\"remove\", job_id=\"abc123\")\n```\n\n## Time Expressions\n\n| User says | Parameters |\n|-----------|------------|\n| every 20 minutes | every_seconds: 1200 |\n| every hour | every_seconds: 3600 |\n| every day at 8am | cron_expr: \"0 8 * * *\" |\n| weekdays at 5pm | cron_expr: \"0 17 * * 1-5\" |\n| at a specific time | at: ISO datetime string (compute from current time) |\n","bot/workspace/skills/tmux/SKILL.md":"---\nname: tmux\ndescription: Remote-control tmux sessions for interactive CLIs by sending keystrokes and scraping pane output.\nmetadata: {\"vikingbot\":{\"emoji\":\"🧵\",\"os\":[\"darwin\",\"linux\"],\"requires\":{\"bins\":[\"tmux\"]}}}\n---\n\n# tmux Skill\n\nUse tmux only when you need an interactive TTY. Prefer exec background mode for long-running, non-interactive tasks.\n\n## Quickstart (isolated socket, exec tool)\n\n```bash\nSOCKET_DIR=\"${NANOBOT_TMUX_SOCKET_DIR:-${TMPDIR:-/tmp}/vikingbot-tmux-sockets}\"\nmkdir -p \"$SOCKET_DIR\"\nSOCKET=\"$SOCKET_DIR/vikingbot.sock\"\nSESSION=vikingbot-python\n\ntmux -S \"$SOCKET\" new -d -s \"$SESSION\" -n shell\ntmux -S \"$SOCKET\" send-keys -t \"$SESSION\":0.0 -- 'PYTHON_BASIC_REPL=1 python3 -q' Enter\ntmux -S \"$SOCKET\" capture-pane -p -J -t \"$SESSION\":0.0 -S -200\n```\n\nAfter starting a session, always print monitor commands:\n\n```\nTo monitor:\n  tmux -S \"$SOCKET\" attach -t \"$SESSION\"\n  tmux -S \"$SOCKET\" capture-pane -p -J -t \"$SESSION\":0.0 -S -200\n```\n\n## Socket convention\n\n- Use `NANOBOT_TMUX_SOCKET_DIR` environment variable.\n- Default socket path: `\"$NANOBOT_TMUX_SOCKET_DIR/vikingbot.sock\"`.\n\n## Targeting panes and naming\n\n- Target format: `session:window.pane` (defaults to `:0.0`).\n- Keep names short; avoid spaces.\n- Inspect: `tmux -S \"$SOCKET\" list-sessions`, `tmux -S \"$SOCKET\" list-panes -a`.\n\n## Finding sessions\n\n- List sessions on your socket: `{baseDir}/scripts/find-sessions.sh -S \"$SOCKET\"`.\n- Scan all sockets: `{baseDir}/scripts/find-sessions.sh --all` (uses `NANOBOT_TMUX_SOCKET_DIR`).\n\n## Sending input safely\n\n- Prefer literal sends: `tmux -S \"$SOCKET\" send-keys -t target -l -- \"$cmd\"`.\n- Control keys: `tmux -S \"$SOCKET\" send-keys -t target C-c`.\n\n## Watching output\n\n- Capture recent history: `tmux -S \"$SOCKET\" capture-pane -p -J -t target -S -200`.\n- Wait for prompts: `{baseDir}/scripts/wait-for-text.sh -t session:0.0 -p 'pattern'`.\n- Attaching is OK; detach with `Ctrl+b d`.\n\n## Spawning processes\n\n- For python REPLs, set `PYTHON_BASIC_REPL=1` (non-basic REPL breaks send-keys flows).\n\n## Windows / WSL\n\n- tmux is supported on macOS/Linux. On Windows, use WSL and install tmux inside WSL.\n- This skill is gated to `darwin`/`linux` and requires `tmux` on PATH.\n\n## Orchestrating Coding Agents (Codex, Claude Code)\n\ntmux excels at running multiple coding agents in parallel:\n\n```bash\nSOCKET=\"${TMPDIR:-/tmp}/codex-army.sock\"\n\n# Create multiple sessions\nfor i in 1 2 3 4 5; do\n  tmux -S \"$SOCKET\" new-session -d -s \"agent-$i\"\ndone\n\n# Launch agents in different workdirs\ntmux -S \"$SOCKET\" send-keys -t agent-1 \"cd /tmp/project1 && codex --yolo 'Fix bug X'\" Enter\ntmux -S \"$SOCKET\" send-keys -t agent-2 \"cd /tmp/project2 && codex --yolo 'Fix bug Y'\" Enter\n\n# Poll for completion (check if prompt returned)\nfor sess in agent-1 agent-2; do\n  if tmux -S \"$SOCKET\" capture-pane -p -t \"$sess\" -S -3 | grep -q \"❯\"; then\n    echo \"$sess: DONE\"\n  else\n    echo \"$sess: Running...\"\n  fi\ndone\n\n# Get full output from completed session\ntmux -S \"$SOCKET\" capture-pane -p -t agent-1 -S -500\n```\n\n**Tips:**\n- Use separate git worktrees for parallel fixes (no branch conflicts)\n- `pnpm install` first before running codex in fresh clones\n- Check for shell prompt (`❯` or `$`) to detect completion\n- Codex needs `--yolo` or `--full-auto` for non-interactive fixes\n\n## Cleanup\n\n- Kill a session: `tmux -S \"$SOCKET\" kill-session -t \"$SESSION\"`.\n- Kill all sessions on a socket: `tmux -S \"$SOCKET\" list-sessions -F '#{session_name}' | xargs -r -n1 tmux -S \"$SOCKET\" kill-session -t`.\n- Remove everything on the private socket: `tmux -S \"$SOCKET\" kill-server`.\n\n## Helper: wait-for-text.sh\n\n`{baseDir}/scripts/wait-for-text.sh` polls a pane for a regex (or fixed string) with a timeout.\n\n```bash\n{baseDir}/scripts/wait-for-text.sh -t session:0.0 -p 'pattern' [-F] [-T 20] [-i 0.5] [-l 2000]\n```\n\n- `-t`/`--target` pane target (required)\n- `-p`/`--pattern` regex to match (required); add `-F` for fixed string\n- `-T` timeout seconds (integer, default 15)\n- `-i` poll interval seconds (default 0.5)\n- `-l` history lines to search (integer, default 1000)\n","bot/workspace/skills/skill-creator/SKILL.md":"---\nname: skill-creator\ndescription: Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.\n---\n\n# Skill Creator\n\nThis skill provides guidance for creating effective skills.\n\n## About Skills\n\nSkills are modular, self-contained packages that extend the agent's capabilities by providing\nspecialized knowledge, workflows, and tools. Think of them as \"onboarding guides\" for specific\ndomains or tasks—they transform the agent from a general-purpose agent into a specialized agent\nequipped with procedural knowledge that no model can fully possess.\n\n### What Skills Provide\n\n1. Specialized workflows - Multi-step procedures for specific domains\n2. Tool integrations - Instructions for working with specific file formats or APIs\n3. Domain expertise - Company-specific knowledge, schemas, business logic\n4. Bundled resources - Scripts, references, and assets for complex and repetitive tasks\n\n## Core Principles\n\n### Concise is Key\n\nThe context window is a public good. Skills share the context window with everything else the agent needs: system prompt, conversation history, other Skills' metadata, and the actual user request.\n\n**Default assumption: the agent is already very smart.** Only add context the agent doesn't already have. Challenge each piece of information: \"Does the agent really need this explanation?\" and \"Does this paragraph justify its token cost?\"\n\nPrefer concise examples over verbose explanations.\n\n### Set Appropriate Degrees of Freedom\n\nMatch the level of specificity to the task's fragility and variability:\n\n**High freedom (text-based instructions)**: Use when multiple approaches are valid, decisions depend on context, or heuristics guide the approach.\n\n**Medium freedom (pseudocode or scripts with parameters)**: Use when a preferred pattern exists, some variation is acceptable, or configuration affects behavior.\n\n**Low freedom (specific scripts, few parameters)**: Use when operations are fragile and error-prone, consistency is critical, or a specific sequence must be followed.\n\nThink of the agent as exploring a path: a narrow bridge with cliffs needs specific guardrails (low freedom), while an open field allows many routes (high freedom).\n\n### Anatomy of a Skill\n\nEvery skill consists of a required SKILL.md file and optional bundled resources:\n\n```\nskill-name/\n├── SKILL.md (required)\n│   ├── YAML frontmatter metadata (required)\n│   │   ├── name: (required)\n│   │   └── description: (required)\n│   └── Markdown instructions (required)\n└── Bundled Resources (optional)\n    ├── scripts/          - Executable code (Python/Bash/etc.)\n    ├── references/       - Documentation intended to be loaded into context as needed\n    └── assets/           - Files used in output (templates, icons, fonts, etc.)\n```\n\n#### SKILL.md (required)\n\nEvery SKILL.md consists of:\n\n- **Frontmatter** (YAML): Contains `name` and `description` fields. These are the only fields that the agent reads to determine when the skill gets used, thus it is very important to be clear and comprehensive in describing what the skill is, and when it should be used.\n- **Body** (Markdown): Instructions and guidance for using the skill. Only loaded AFTER the skill triggers (if at all).\n\n#### Bundled Resources (optional)\n\n##### Scripts (`scripts/`)\n\nExecutable code (Python/Bash/etc.) for tasks that require deterministic reliability or are repeatedly rewritten.\n\n- **When to include**: When the same code is being rewritten repeatedly or deterministic reliability is needed\n- **Example**: `scripts/rotate_pdf.py` for PDF rotation tasks\n- **Benefits**: Token efficient, deterministic, may be executed without loading into context\n- **Note**: Scripts may still need to be read by the agent for patching or environment-specific adjustments\n\n##### References (`references/`)\n\nDocumentation and reference material intended to be loaded as needed into context to inform the agent's process and thinking.\n\n- **When to include**: For documentation that the agent should reference while working\n- **Examples**: `references/finance.md` for financial schemas, `references/mnda.md` for company NDA template, `references/policies.md` for company policies, `references/api_docs.md` for API specifications\n- **Use cases**: Database schemas, API documentation, domain knowledge, company policies, detailed workflow guides\n- **Benefits**: Keeps SKILL.md lean, loaded only when the agent determines it's needed\n- **Best practice**: If files are large (>10k words), include grep search patterns in SKILL.md\n- **Avoid duplication**: Information should live in either SKILL.md or references files, not both. Prefer references files for detailed information unless it's truly core to the skill—this keeps SKILL.md lean while making information discoverable without hogging the context window. Keep only essential procedural instructions and workflow guidance in SKILL.md; move detailed reference material, schemas, and examples to references files.\n\n##### Assets (`assets/`)\n\nFiles not intended to be loaded into context, but rather used within the output the agent produces.\n\n- **When to include**: When the skill needs files that will be used in the final output\n- **Examples**: `assets/logo.png` for brand assets, `assets/slides.pptx` for PowerPoint templates, `assets/frontend-template/` for HTML/React boilerplate, `assets/font.ttf` for typography\n- **Use cases**: Templates, images, icons, boilerplate code, fonts, sample documents that get copied or modified\n- **Benefits**: Separates output resources from documentation, enables the agent to use files without loading them into context\n\n#### What to Not Include in a Skill\n\nA skill should only contain essential files that directly support its functionality. Do NOT create extraneous documentation or auxiliary files, including:\n\n- README.md\n- INSTALLATION_GUIDE.md\n- QUICK_REFERENCE.md\n- CHANGELOG.md\n- etc.\n\nThe skill should only contain the information needed for an AI agent to do the job at hand. It should not contain auxiliary context about the process that went into creating it, setup and testing procedures, user-facing documentation, etc. Creating additional documentation files just adds clutter and confusion.\n\n### Progressive Disclosure Design Principle\n\nSkills use a three-level loading system to manage context efficiently:\n\n1. **Metadata (name + description)** - Always in context (~100 words)\n2. **SKILL.md body** - When skill triggers (<5k words)\n3. **Bundled resources** - As needed by the agent (Unlimited because scripts can be executed without reading into context window)\n\n#### Progressive Disclosure Patterns\n\nKeep SKILL.md body to the essentials and under 500 lines to minimize context bloat. Split content into separate files when approaching this limit. When splitting out content into other files, it is very important to reference them from SKILL.md and describe clearly when to read them, to ensure the reader of the skill knows they exist and when to use them.\n\n**Key principle:** When a skill supports multiple variations, frameworks, or options, keep only the core workflow and selection guidance in SKILL.md. Move variant-specific details (patterns, examples, configuration) into separate reference files.\n\n**Pattern 1: High-level guide with references**\n\n```markdown\n# PDF Processing\n\n## Quick start\n\nExtract text with pdfplumber:\n[code example]\n\n## Advanced features\n\n- **Form filling**: See [FORMS.md](FORMS.md) for complete guide\n- **API reference**: See [REFERENCE.md](REFERENCE.md) for all methods\n- **Examples**: See [EXAMPLES.md](EXAMPLES.md) for common patterns\n```\n\nthe agent loads FORMS.md, REFERENCE.md, or EXAMPLES.md only when needed.\n\n**Pattern 2: Domain-specific organization**\n\nFor Skills with multiple domains, organize content by domain to avoid loading irrelevant context:\n\n```\nbigquery-skill/\n├── SKILL.md (overview and navigation)\n└── reference/\n    ├── finance.md (revenue, billing metrics)\n    ├── sales.md (opportunities, pipeline)\n    ├── product.md (API usage, features)\n    └── marketing.md (campaigns, attribution)\n```\n\nWhen a user asks about sales metrics, the agent only reads sales.md.\n\nSimilarly, for skills supporting multiple frameworks or variants, organize by variant:\n\n```\ncloud-deploy/\n├── SKILL.md (workflow + provider selection)\n└── references/\n    ├── aws.md (AWS deployment patterns)\n    ├── gcp.md (GCP deployment patterns)\n    └── azure.md (Azure deployment patterns)\n```\n\nWhen the user chooses AWS, the agent only reads aws.md.\n\n**Pattern 3: Conditional details**\n\nShow basic content, link to advanced content:\n\n```markdown\n# DOCX Processing\n\n## Creating documents\n\nUse docx-js for new documents. See [DOCX-JS.md](DOCX-JS.md).\n\n## Editing documents\n\nFor simple edits, modify the XML directly.\n\n**For tracked changes**: See [REDLINING.md](REDLINING.md)\n**For OOXML details**: See [OOXML.md](OOXML.md)\n```\n\nthe agent reads REDLINING.md or OOXML.md only when the user needs those features.\n\n**Important guidelines:**\n\n- **Avoid deeply nested references** - Keep references one level deep from SKILL.md. All reference files should link directly from SKILL.md.\n- **Structure longer reference files** - For files longer than 100 lines, include a table of contents at the top so the agent can see the full scope when previewing.\n\n## Skill Creation Process\n\nSkill creation involves these steps:\n\n1. Understand the skill with concrete examples\n2. Plan reusable skill contents (scripts, references, assets)\n3. Initialize the skill (run init_skill.py)\n4. Edit the skill (implement resources and write SKILL.md)\n5. Package the skill (run package_skill.py)\n6. Iterate based on real usage\n\nFollow these steps in order, skipping only if there is a clear reason why they are not applicable.\n\n### Skill Naming\n\n- Use lowercase letters, digits, and hyphens only; normalize user-provided titles to hyphen-case (e.g., \"Plan Mode\" -> `plan-mode`).\n- When generating names, generate a name under 64 characters (letters, digits, hyphens).\n- Prefer short, verb-led phrases that describe the action.\n- Namespace by tool when it improves clarity or triggering (e.g., `gh-address-comments`, `linear-address-issue`).\n- Name the skill folder exactly after the skill name.\n\n### Step 1: Understanding the Skill with Concrete Examples\n\nSkip this step only when the skill's usage patterns are already clearly understood. It remains valuable even when working with an existing skill.\n\nTo create an effective skill, clearly understand concrete examples of how the skill will be used. This understanding can come from either direct user examples or generated examples that are validated with user feedback.\n\nFor example, when building an image-editor skill, relevant questions include:\n\n- \"What functionality should the image-editor skill support? Editing, rotating, anything else?\"\n- \"Can you give some examples of how this skill would be used?\"\n- \"I can imagine users asking for things like 'Remove the red-eye from this image' or 'Rotate this image'. Are there other ways you imagine this skill being used?\"\n- \"What would a user say that should trigger this skill?\"\n\nTo avoid overwhelming users, avoid asking too many questions in a single message. Start with the most important questions and follow up as needed for better effectiveness.\n\nConclude this step when there is a clear sense of the functionality the skill should support.\n\n### Step 2: Planning the Reusable Skill Contents\n\nTo turn concrete examples into an effective skill, analyze each example by:\n\n1. Considering how to execute on the example from scratch\n2. Identifying what scripts, references, and assets would be helpful when executing these workflows repeatedly\n\nExample: When building a `pdf-editor` skill to handle queries like \"Help me rotate this PDF,\" the analysis shows:\n\n1. Rotating a PDF requires re-writing the same code each time\n2. A `scripts/rotate_pdf.py` script would be helpful to store in the skill\n\nExample: When designing a `frontend-webapp-builder` skill for queries like \"Build me a todo app\" or \"Build me a dashboard to track my steps,\" the analysis shows:\n\n1. Writing a frontend webapp requires the same boilerplate HTML/React each time\n2. An `assets/hello-world/` template containing the boilerplate HTML/React project files would be helpful to store in the skill\n\nExample: When building a `big-query` skill to handle queries like \"How many users have logged in today?\" the analysis shows:\n\n1. Querying BigQuery requires re-discovering the table schemas and relationships each time\n2. A `references/schema.md` file documenting the table schemas would be helpful to store in the skill\n\nTo establish the skill's contents, analyze each concrete example to create a list of the reusable resources to include: scripts, references, and assets.\n\n### Step 3: Initializing the Skill\n\nAt this point, it is time to actually create the skill.\n\nSkip this step only if the skill being developed already exists, and iteration or packaging is needed. In this case, continue to the next step.\n\nWhen creating a new skill from scratch, always run the `init_skill.py` script. The script conveniently generates a new template skill directory that automatically includes everything a skill requires, making the skill creation process much more efficient and reliable.\n\nUsage:\n\n```bash\nscripts/init_skill.py <skill-name> --path <output-directory> [--resources scripts,references,assets] [--examples]\n```\n\nExamples:\n\n```bash\nscripts/init_skill.py my-skill --path skills/public\nscripts/init_skill.py my-skill --path skills/public --resources scripts,references\nscripts/init_skill.py my-skill --path skills/public --resources scripts --examples\n```\n\nThe script:\n\n- Creates the skill directory at the specified path\n- Generates a SKILL.md template with proper frontmatter and TODO placeholders\n- Optionally creates resource directories based on `--resources`\n- Optionally adds example files when `--examples` is set\n\nAfter initialization, customize the SKILL.md and add resources as needed. If you used `--examples`, replace or delete placeholder files.\n\n### Step 4: Edit the Skill\n\nWhen editing the (newly-generated or existing) skill, remember that the skill is being created for another instance of the agent to use. Include information that would be beneficial and non-obvious to the agent. Consider what procedural knowledge, domain-specific details, or reusable assets would help another the agent instance execute these tasks more effectively.\n\n#### Learn Proven Design Patterns\n\nConsult these helpful guides based on your skill's needs:\n\n- **Multi-step processes**: See references/workflows.md for sequential workflows and conditional logic\n- **Specific output formats or quality standards**: See references/output-patterns.md for template and example patterns\n\nThese files contain established best practices for effective skill design.\n\n#### Start with Reusable Skill Contents\n\nTo begin implementation, start with the reusable resources identified above: `scripts/`, `references/`, and `assets/` files. Note that this step may require user input. For example, when implementing a `brand-guidelines` skill, the user may need to provide brand assets or templates to store in `assets/`, or documentation to store in `references/`.\n\nAdded scripts must be tested by actually running them to ensure there are no bugs and that the output matches what is expected. If there are many similar scripts, only a representative sample needs to be tested to ensure confidence that they all work while balancing time to completion.\n\nIf you used `--examples`, delete any placeholder files that are not needed for the skill. Only create resource directories that are actually required.\n\n#### Update SKILL.md\n\n**Writing Guidelines:** Always use imperative/infinitive form.\n\n##### Frontmatter\n\nWrite the YAML frontmatter with `name` and `description`:\n\n- `name`: The skill name\n- `description`: This is the primary triggering mechanism for your skill, and helps the agent understand when to use the skill.\n  - Include both what the Skill does and specific triggers/contexts for when to use it.\n  - Include all \"when to use\" information here - Not in the body. The body is only loaded after triggering, so \"When to Use This Skill\" sections in the body are not helpful to the agent.\n  - Example description for a `docx` skill: \"Comprehensive document creation, editing, and analysis with support for tracked changes, comments, formatting preservation, and text extraction. Use when the agent needs to work with professional documents (.docx files) for: (1) Creating new documents, (2) Modifying or editing content, (3) Working with tracked changes, (4) Adding comments, or any other document tasks\"\n\nDo not include any other fields in YAML frontmatter.\n\n##### Body\n\nWrite instructions for using the skill and its bundled resources.\n\n### Step 5: Packaging a Skill\n\nOnce development of the skill is complete, it must be packaged into a distributable .skill file that gets shared with the user. The packaging process automatically validates the skill first to ensure it meets all requirements:\n\n```bash\nscripts/package_skill.py <path/to/skill-folder>\n```\n\nOptional output directory specification:\n\n```bash\nscripts/package_skill.py <path/to/skill-folder> ./dist\n```\n\nThe packaging script will:\n\n1. **Validate** the skill automatically, checking:\n\n   - YAML frontmatter format and required fields\n   - Skill naming conventions and directory structure\n   - Description completeness and quality\n   - File organization and resource references\n\n2. **Package** the skill if validation passes, creating a .skill file named after the skill (e.g., `my-skill.skill`) that includes all files and maintains the proper directory structure for distribution. The .skill file is a zip file with a .skill extension.\n\nIf validation fails, the script will report the errors and exit without creating a package. Fix any validation errors and run the packaging command again.\n\n### Step 6: Iterate\n\nAfter testing the skill, users may request improvements. Often this happens right after using the skill, with fresh context of how the skill performed.\n\n**Iteration workflow:**\n\n1. Use the skill on real tasks\n2. Notice struggles or inefficiencies\n3. Identify how SKILL.md or bundled resources should be updated\n4. Implement changes and test again\n","bot/workspace/skills/weather/SKILL.md":"---\nname: weather\ndescription: Get current weather and forecasts (no API key required).\nhomepage: https://wttr.in/:help\nmetadata: {\"vikingbot\":{\"emoji\":\"🌤️\",\"requires\":{\"bins\":[\"curl\"]}}}\n---\n\n# Weather\n\nTwo free services, no API keys needed.\n\n## wttr.in (primary)\n\nQuick one-liner:\n```bash\ncurl -s \"wttr.in/London?format=3\"\n# Output: London: ⛅️ +8°C\n```\n\nCompact format:\n```bash\ncurl -s \"wttr.in/London?format=%l:+%c+%t+%h+%w\"\n# Output: London: ⛅️ +8°C 71% ↙5km/h\n```\n\nFull forecast:\n```bash\ncurl -s \"wttr.in/London?T\"\n```\n\nFormat codes: `%c` condition · `%t` temp · `%h` humidity · `%w` wind · `%l` location · `%m` moon\n\nTips:\n- URL-encode spaces: `wttr.in/New+York`\n- Airport codes: `wttr.in/JFK`\n- Units: `?m` (metric) `?u` (USCS)\n- Today only: `?1` · Current only: `?0`\n- PNG: `curl -s \"wttr.in/Berlin.png\" -o /tmp/weather.png`\n\n## Open-Meteo (fallback, JSON)\n\nFree, no key, good for programmatic use:\n```bash\ncurl -s \"https://api.open-meteo.com/v1/forecast?latitude=51.5&longitude=-0.12&current_weather=true\"\n```\n\nFind coordinates for a city, then query. Returns JSON with temp, windspeed, weathercode.\n\nDocs: https://open-meteo.com/en/docs\n","examples/openclaw-plugin/skills/install-openviking-memory/SKILL.md":"---\nname: install-openviking-memory\ndescription: >\n  Install and configure the OpenViking long-term memory plugin for OpenClaw via natural conversation.\n  Once installed, the plugin automatically captures facts from chats and recalls relevant context\n  before each reply (auto-capture + auto-recall, cross-session). Covers prerequisites, install through\n  OpenClaw's plugin manager (ov-install as backup), wizard-based configuration, slot activation,\n  gateway restart, verification, multi-tenant root keys, multi-instance and uninstall. The user needs\n  no CLI knowledge — the agent runs everything and only asks for a few values. Assumes the OpenViking\n  server is already running; it does NOT install the server. Trigger on: \"install OpenViking\",\n  \"set up memory\", \"configure memory plugin\", \"add long-term memory\", \"semantic memory\", \"RAG\",\n  \"帮我装 OpenViking\", \"配置记忆插件\", \"安装记忆功能\", \"接入 OpenViking\".\nversion: 2026.6.5\nmetadata:\n  openclaw:\n    requires:\n      bins:\n        - node\n        - openclaw\n  emoji: \"🦣\"\n  homepage: \"https://github.com/volcengine/OpenViking\"\ntags:\n  - install\n  - setup\n  - memory\n  - openviking\n  - rag\n  - long-term-memory\n---\n\n# Install OpenViking Memory — Agent Installation & Reference Skill\n\nYou are about to help a user install and configure the OpenViking memory plugin for OpenClaw.\nThe 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.\n\n**Read this entire document before starting. Follow every step in order. Do NOT skip steps.**\n\nThis skill is split into two halves:\n\n- **Part 1 (STEP 0–10): Natural-language installation walkthrough.** Use this on first install or reconfigure.\n- **Part 2: Reference.** Tools, config schema, multi-tenant, multi-instance, daily ops, uninstall, error recovery. Read on demand.\n\n> **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.\n\n---\n\n# Part 1 — Natural-Language Installation Walkthrough\n\n## STEP 0: Detect Language\n\nIf the user's first message contains Chinese characters, respond in **Chinese** throughout.\nOtherwise respond in **English**.\nAll user-facing messages below have (CN)/(EN) variants — use the matching one.\n\nDo NOT show this step to the user.\n\n---\n\n## STEP 1: Detect Operating System (silently)\n\nRun this command and remember the result. Do NOT show it to the user.\n\n```bash\nuname -s 2>/dev/null || echo WINDOWS\n```\n\n- Output contains `Darwin` → `OS=mac`\n- Output contains `Linux` → `OS=linux`\n- Output is `WINDOWS` or the command fails → `OS=windows`\n\n---\n\n## STEP 2: Check Prerequisites (silently)\n\nRun silently:\n\n```bash\nnode -v\nopenclaw --version\n```\n\n**If `node` is missing:**\n\n> (CN) 你的系统没有安装 Node.js。OpenClaw 和 OpenViking 插件需要 Node.js >= 22。请先安装 Node.js，然后再回来找我。\n> (EN) Node.js is not installed. OpenClaw and the OpenViking plugin require Node.js >= 22. Please install Node.js first, then come back.\n\n**Stop. Do NOT continue.**\n\n**If `openclaw` is missing:**\n\n> (CN) 你的系统没有安装 OpenClaw。请先安装 OpenClaw（>= 2026.5.27），然后再回来找我。\n> (EN) OpenClaw is not installed. Please install OpenClaw (>= 2026.5.27) first, then come back.\n\n**Stop. Do NOT continue.**\n\nIf both exist, proceed to STEP 3 silently.\n\n---\n\n## STEP 3: Greet and Ask for 3 Values\n\nSend this message:\n\n> (CN) 好，我来帮你接入 OpenViking 长期记忆。装好之后，我会自动记住对话里的重要信息，下次聊也能回忆起来。\n>\n> 我需要 3 条信息，不知道的可以问你的管理员：\n> 1. **OpenViking 服务地址** —— 例如 `https://ov.example.com` 或 `http://192.168.1.100:1933`，本机服务可以直接说\"本机\"\n> 2. **API Key** —— 用来鉴权；服务没开认证可以说\"没有\"\n> 3. **记忆归属**（可选） —— 所有对话共用 user 记忆（默认），还是在 `peers/<assistant_id>` 或 `peers/<sender_id>` 下分开存放\n>\n> 先告诉我服务地址吧？\n\n> (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.\n>\n> I need 3 things (ask your admin if unsure):\n> 1. **OpenViking server URL** — e.g. `https://ov.example.com` or `http://192.168.1.100:1933`. For a local server, just say \"local\".\n> 2. **API Key** — for auth. Say \"none\" if the server has no auth.\n> 3. **Memory scope** (optional) — shared user memory (default), or peer memory under `peers/<assistant_id>` / `peers/<sender_id>`.\n>\n> What's the server URL?\n\n---\n\n## STEP 4: Collect Values\n\nCollect 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.\n\n### 4a. `BASE_URL` (REQUIRED)\n\n- \"local\" / \"本机\" / \"localhost\" → use `http://127.0.0.1:1933`.\n- `ov.example.com` without protocol → prepend `https://`.\n- Strip trailing `/`, `/health`, or `/api`.\n- After normalization must start with `http://` or `https://`.\n- 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.**\n\n### 4b. `API_KEY` (OPTIONAL)\n\n> (CN) API Key 是什么？服务没开认证就直接说\"没有\"。\n> (EN) What's the API Key? Say \"none\" if the server has no auth.\n\n- \"no\" / \"none\" / \"没有\" / \"空\" / empty → `API_KEY=\"\"` (you will skip the flag later).\n- Otherwise store as-is.\n\n### 4c. `PEER_ROLE` (OPTIONAL)\n\n> (CN) 记忆要存在哪一层？默认共享在 `viking://user/<user_id>/memories`（`none`）。也可以按 OpenClaw 助手存到 `.../peers/<assistant_id>/memories`（`assistant`），或按发送者存到 `.../peers/<sender_id>/memories`（`sender`）。\n> (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`).\n\nDefault to `none`. **Never silently pick another value** — the three options mean different storage layouts and are awkward to change later:\n\n| Value | Concrete layout | Choose it when |\n|---|---|---|\n| `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. |\n| `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/...`. |\n| `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. |\n\n`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`.\n\nOpenViking 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.\n\n- Empty / \"default\" / \"默认\" / \"共用\" → leave unset (plugin defaults to `none`).\n- Otherwise pass `--peer-role none|assistant|sender`. Treat explicit legacy `person` as `sender`. Any other value → ask again.\n\n### 4d. `PEER_PREFIX` (OPTIONAL, only when `PEER_ROLE=assistant`)\n\n> (CN) 想给这个 agent 一个记忆前缀吗？留空就用默认。只能用字母、数字、`_`、`-`。\n> (EN) Want to set an peer prefix? Leave blank for the default. Letters, digits, `_`, `-` only.\n\n- Only meaningful when `PEER_ROLE=assistant`; skip the question otherwise.\n- Empty / \"default\" / \"默认\" → leave unset (plugin defaults to `\"\"`).\n- Otherwise validate against `/^[A-Za-z0-9_-]+$/`. If invalid, ask again.\n\n### 4e. (Conditional) Multi-Tenant Root-Key Fields\n\nOnly ask for these if STEP 7 detects a root key (`Root API key detected. Missing: --account-id, --user-id`). Don't ask up front.\n\n- `ACCOUNT_ID`\n- `USER_ID`\n\nSee **Reference: Multi-Tenant** for what these mean.\n\n---\n\n## STEP 5: Pre-flight Connectivity Check (silently)\n\nTell the user briefly:\n\n> (CN) 我先测一下能不能连上服务……\n> (EN) Let me test the connection to your server...\n\nRun:\n\n**If OS=windows:**\n\n```powershell\ntry { (Invoke-WebRequest -Uri \"BASE_URL/health\" -TimeoutSec 10 -UseBasicParsing -ErrorAction Stop).StatusCode } catch { $_.Exception.Response.StatusCode.value__ }\n```\n\n**If OS=mac or OS=linux:**\n\n```bash\ncurl -sS -o /dev/null -w \"%{http_code}\" --connect-timeout 10 \"BASE_URL/health\"\n```\n\nReplace `BASE_URL` with the actual value.\n\n| Status | Meaning | Action |\n|---|---|---|\n| `200` | Server reachable, no auth on `/health` | Proceed to STEP 6. |\n| `401` / `403` | Server reachable but `/health` requires auth | Proceed to STEP 6 — the wizard's key probe will sort it out. |\n| `000` / timeout / connection refused | Server unreachable | **Server-not-ready handling** below. |\n| Anything else | Unexpected | Show status code to the user, go back to STEP 4a. |\n\n### Server-not-ready handling\n\nThis skill **does not install or operate the OpenViking server**. If the user's server is unreachable, present the situation honestly and offer two paths:\n\n> (CN) ❌ 我连不上 `BASE_URL`。可能是：\n> 1) 服务还没启动 —— 请联系你的 OpenViking 服务管理员把它起起来；如果是你自己负责，请参考 OpenViking 官方文档（`https://github.com/volcengine/OpenViking`）的 server 启动指引。\n> 2) 地址不对 —— 你可以重新告诉我正确的地址。\n> 3) 网络不通（防火墙 / VPN / 内网）—— 你确认一下网络。\n>\n> 也可以选择\"先把配置写下来\"，等服务起来就自动生效，要这么办吗？\n\n> (EN) ❌ Cannot reach `BASE_URL`. Likely cause:\n> 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.**\n> 2) **Wrong URL** — give me the correct URL.\n> 3) **Network blocked** (firewall / VPN / private network) — please verify connectivity.\n>\n> Or I can save the config now (`--allow-offline`) so it will activate automatically once the server is up. Want me to do that?\n\nIf the user fixes the URL → back to STEP 4a.\nIf the user wants `--allow-offline` → remember `ALLOW_OFFLINE=true` and continue to STEP 6.\nIf the user gives up / cannot fix → stop here. Do NOT continue with a broken state.\n\n---\n\n## STEP 6: Install the Plugin\n\nThe 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.\n\n### Path A — Primary: `openclaw plugins install` (uses ClawHub)\n\nTell the user:\n\n> (CN) 现在开始装插件……\n> (EN) Installing the plugin now...\n\nRun:\n\n```bash\nopenclaw plugins install clawhub:@openviking/openclaw-plugin\n```\n\nTrigger fallback to Path B only if the output contains any of these strings:\n\n- `429`\n- `rate limit` / `rate-limited` / `Too Many Requests`\n- `not logged in` / `please log in` / `please login` / `unauthorized` / `401` / `403` together with `clawhub`\n- `ETIMEDOUT` / `ECONNRESET` on a `clawhub`-related host\n- generic message indicating the registry refused an anonymous client\n\nBefore falling back, also try the explicit registry prefix once:\n\n```bash\nopenclaw plugins install clawhub:@openviking/openclaw-plugin\n```\n\nIf the install **succeeds**, jump to STEP 7.\n\nIf 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.\n\n### Path B — Backup: `ov-install` (bypasses ClawHub)\n\nTell the user:\n\n> (CN) ClawHub 现在好像被限流、不可用，或者当前账号不能安装。我改用备用路径，通过 npm 下载并部署插件包。\n> (EN) ClawHub looks rate-limited, unavailable, or blocked for this account. I'll use the backup path and install the plugin package from npm.\n\nRun the installer with `npx` (no global install needed):\n\n```bash\nnpx -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]\n```\n\nBuild the flag list according to what the user gave you:\n\n- Always pass `--base-url BASE_URL`.\n- Pass `--api-key API_KEY` only if `API_KEY` is non-empty.\n- 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.\n- Pass `--peer-prefix PEER_PREFIX` only if the user gave one and `PEER_ROLE=assistant`.\n- `--account-id` / `--user-id` only if the root-key path requires them.\n\n`ov-install` will, in one shot:\n1. Download the `@openviking/openclaw-plugin` package from npm into a temporary staging dir.\n2. Copy the package into the OpenClaw `extensions/` dir and install plugin dependencies.\n3. Register the plugin in `openclaw.json` (via `openclaw plugins enable` or direct write).\n4. Run `openclaw openviking setup --json --base-url … [--api-key …]` for the user.\n5. Return a non-zero exit if setup needs explicit `--allow-offline` or `--force-slot` consent.\n\nThis 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).\n\nIf `ov-install` exits non-zero, capture the last 30 lines of its output, show them to the user, and stop. Don't retry blindly.\n\n---\n\n## STEP 7: Configure (only on Path A — Path B did this for you)\n\nRun the setup wizard non-interactively. Build flags from collected values:\n\n```bash\nopenclaw 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]\n```\n\nRules:\n\n- `--base-url BASE_URL` is **required** under `--json`. Without it, the wizard prints `--json requires --base-url for non-interactive mode`.\n- `--api-key` only if `API_KEY` is non-empty.\n- `--peer-role` only if the user chose `assistant` or `sender`; omit it for the `none` default. Normalize legacy `person` to `sender` first.\n- `--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.\n- `--account-id` / `--user-id` only after STEP 7 root-key detection (see below).\n- `--allow-offline` only if the user explicitly approved it in STEP 5.\n- `--force-slot` **never** in the first attempt. Add only after the user confirms (see slot_blocked handling below).\n\n### Parse the JSON output\n\nThe wizard prints a single JSON object:\n\n```json\n{\n  \"success\": true | false,\n  \"action\": \"configured\" | \"existing\" | \"error\" | \"slot_blocked\",\n  \"config\": { \"mode\": \"remote\", \"baseUrl\": \"...\", \"apiKey\": \"...\", \"peer_prefix\": \"...\", \"accountId\": \"...\", \"userId\": \"...\" },\n  \"health\": { \"ok\": true, \"version\": \"...\", \"compatibility\": \"compatible\" | \"server_too_old\" | \"server_too_new\" | \"unknown\" },\n  \"keyProbe\": { \"keyType\": \"user_key\" | \"root_key\" | \"no_key\" | \"unknown\", \"needsAccountId\": false, \"needsUserId\": false, \"detail\": \"...\" },\n  \"slot\": { \"activated\": true, \"replaced\": false, \"previousOwner\": \"openviking\" },\n  \"error\": \"...\"\n}\n```\n\n### Decision matrix\n\n| Condition | Action |\n|---|---|\n| `success: true` and `action: \"configured\"` or `\"existing\"` | Done. Proceed to STEP 9. |\n| `success: false` and `action: \"slot_blocked\"` | **Slot conflict — see below.** |\n| `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. |\n| `success: false` and `error` contains `\"Root API key detected\"` and `\"Missing: --account-id, --user-id\"` | **Root-key path — see below.** |\n| `success: false` and `error` contains `\"Invalid API key\"` / `keyProbe.keyType: \"unknown\"` after auth failure detail | API key wrong. Back to STEP 4b. |\n| `success: false` and any other `action: \"error\"` | Show `error` to the user, stop. Do NOT pretend success. |\n\n### Slot conflict (`slot_blocked`)\n\nThe error text looks like:\n\n> `contextEngine slot is owned by \"<other-plugin>\". Config was saved but slot was NOT changed. Use --force-slot to replace.`\n\n**Do NOT silently use `--force-slot`.** Ask the user:\n\n> (CN) 你的 OpenClaw 当前 contextEngine 槽被 `<other-plugin>` 占着。如果用 OpenViking 替换它，`<other-plugin>` 就不再生效。要替换吗？\n> (EN) Your `contextEngine` slot is currently owned by `<other-plugin>`. Activating OpenViking will disable it. Replace?\n\nIf 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.\n\n### Root-key path\n\nThe error text looks like:\n\n> `Root API key detected. Missing: --account-id, --user-id. Provide both to enable multi-tenant scoping.`\n\nAsk the user:\n\n> (CN) 你给的是 root 级 API Key，需要再补两个值才能用：账户 ID（accountId）和用户 ID（userId）。这两个一般是 OpenViking 服务管理员配的，不知道就问他们。\n> (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.\n\nAfter collecting, retry the setup command with `--account-id` and `--user-id` appended.\n\n---\n\n## STEP 8: (Reserved — done inside STEP 7 or by `ov-install`)\n\nThe 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.\n\n---\n\n## STEP 9: Restart the Gateway\n\n```bash\nopenclaw gateway restart\n```\n\nIf it fails, try once more with:\n\n```bash\nopenclaw gateway --force\n```\n\nIf both fail:\n\n> (CN) Gateway 没能自动重启。请你手动跑一下 `openclaw gateway restart`。重启完告诉我，我来验证。\n> (EN) Gateway didn't restart cleanly. Please run `openclaw gateway restart` manually, then tell me when it's done so I can verify.\n\nWait ~3 seconds before STEP 10.\n\n---\n\n## STEP 10: Verify\n\n```bash\nopenclaw openviking status --json\n```\n\nExpected output:\n\n```json\n{\n  \"configured\": true,\n  \"slotActive\": true,\n  \"health\": { \"ok\": true },\n  \"config\": { \"baseUrl\": \"...\", \"peer_prefix\": \"...\" }\n}\n```\n\n### Decision\n\n- `configured: true` **and** `slotActive: true` **and** `health.ok: true` → **success.** Send the success message below.\n- `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.\n- `configured: true` but `health.ok: false` → server became unreachable. Tell the user; the config will start working as soon as the server is back.\n- `configured: false` → setup didn't actually persist. Show full status JSON to the user and stop.\n\n### Success message\n\n> (CN) 🎉 全部搞定！OpenViking 长期记忆已经接好了。\n>\n> 工作方式：\n> - **每一轮**：你的对话会被自动追加到 OpenViking session 里；自动抽取依赖阈值 commit 或 `/compact`\n> - **明确要记住时**：如果你说“记住/保存/存一下”某个长期事实，我会用 `memory_store` 立即提交到记忆管线\n> - **`/compact` 时**：待提交的 session 消息会在 OpenViking 服务端被抽取成长期记忆\n> - **后续会话**：每次回复前我都会自动搜一下相关记忆并带进上下文\n>\n> 想验证一下吗？可以直接说“记住我的邮箱是 test@example.com”，让我通过 `memory_store` 立即提交；或者先聊几句包含可记忆事实的话，再调一下 `/compact`。之后开新对话问“我的邮箱是？”，应该就能召回了。\n\n> (EN) 🎉 All set! OpenViking long-term memory is connected.\n>\n> How it works:\n> - **Every turn**: our messages are appended to an OpenViking session; automatic extraction depends on a threshold commit or `/compact`\n> - **Explicit remember requests**: if you ask me to remember/save/store a long-term fact, I can use `memory_store` to commit it immediately\n> - **On `/compact`**: pending session messages are extracted into long-term memories on the OpenViking server\n> - **Future sessions**: relevant memories are auto-retrieved and injected before my replies\n>\n> 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.\n\n---\n\n# Part 2 — Reference\n\n## How It Works\n\nThe 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:\n\n- **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/`.\n- **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.\n  - `captureMode: \"semantic\"` (default): server extraction pipeline filters all qualifying text.\n  - `captureMode: \"keyword\"`: only text matching trigger words (e.g. \"remember\", \"preference\") is considered.\n- **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.\n\n**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.\n\n### Explicit long-term memory writes\n\nAuto-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.\n\nUse `memory_store` as the integration-side reliable path for durable-memory intent:\n\n- It writes the supplied text into an OpenViking session and calls `commit(wait=true)`.\n- It complements auto-capture; it does not replace normal session capture.\n- 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.\n\n## Available Tools\n\nThese are the plugin tools the agent can call once installed.\n\n### `memory_recall` — Search Memories\n\n| Parameter | Required | Description |\n|---|---|---|\n| `query` | Yes | Search query text |\n| `limit` | No | Maximum number of results (defaults to plugin config) |\n| `scoreThreshold` | No | Minimum relevance score 0–1 (defaults to plugin config) |\n| `targetUri` | No | Exact search scope URI. If provided, only that URI is searched. |\n| `resourceTypes` | No | Array of target types used when `targetUri` is omitted: `resource`, `user`, `agent`. Defaults to plugin `recallTargetTypes`. |\n\nExample: user asks \"What programming language did I say I like?\"\n\n### `memory_store` — Manual Store\n\n| Parameter | Required | Description |\n|---|---|---|\n| `text` | Yes | Information text to store |\n| `role` | No | Session role (default `user`) |\n| `sessionId` | No | Existing OpenViking session ID |\n\nUse this when the user explicitly asks to remember/save/store a long-term fact, preference, project, or decision.\n\nExample: user says \"Remember my email is xxx@example.com\".\n\n### `memory_forget` — Delete Memories\n\n| Parameter | Required | Description |\n|---|---|---|\n| `uri` | No | Exact memory URI (direct delete) |\n| `query` | No | Search query (find then delete) |\n| `targetUri` | No | Search scope URI |\n| `limit` | No | Search limit (default 5) |\n| `scoreThreshold` | No | Minimum relevance score |\n\nExample: user says \"Forget my phone number\".\n\n### `ov_archive_search` — Search Archived Original Messages\n\n| Parameter | Required | Description |\n|---|---|---|\n| `query` | Yes | A single keyword or short phrase. Prefer concrete names, dates, commands, paths, or distinctive nouns. |\n| `archiveId` | No | Optional archive ID such as `archive_002`. |\n\nUse when `[Session History Summary]` is too coarse. Try at least two keyword variants before concluding the detail is unavailable.\n\n### `ov_archive_expand` — Expand An Archive\n\n| Parameter | Required | Description |\n|---|---|---|\n| `archiveId` | Yes | Archive ID from `[Archive Index]`, e.g. `archive_002`. |\n\n### `add_resource` — Import Resource\n\nThe 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.\n\n| Parameter | Required | Description |\n|---|---|---|\n| `source` | Yes | Local path, OpenClaw media attachment path, directory path, public URL, or Git URL. |\n| `to` | No | Exact target URI under `viking://resources`; mutually exclusive with `parent`. |\n| `parent` | No | Parent URI under `viking://resources`; mutually exclusive with `to`. |\n| `reason` | No | Reason or note for import. |\n| `instruction` | No | Processing instruction for semantic extraction. |\n| `wait` | No | Wait for processing completion. |\n\n### `add_skill` — Import Agent Skill\n\n| Parameter | Required | Description |\n|---|---|---|\n| `source` | No | Local `SKILL.md` path or skill directory. Exactly one of `source` or `data` is required. |\n| `data` | No | Raw `SKILL.md` content or MCP tool dict. Exactly one of `source` or `data` is required. |\n| `wait` | No | Wait for processing completion. |\n\nAgent 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.\n\n### `ov_search` — Search Resources and Skills\n\n| Parameter | Required | Description |\n|---|---|---|\n| `query` | Yes | Search query. |\n| `uri` | No | Optional search URI. Defaults to resources plus agent skills. |\n| `limit` | No | Max results per search scope. Default: 10. |\n\nImportant: `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.\n\n### `ov_read` — Read OpenViking Content\n\n| Parameter | Required | Description |\n|---|---|---|\n| `uri` | Yes | Exact `viking://...` URI returned by `ov_search` or recall trace results. Local file paths and `openviking://...` display aliases are refused. |\n\n### `ov_recall_trace` — Query Recall Trace\n\n| Parameter | Required | Description |\n|---|---|---|\n| `turn` | No | `latest` or `all`; default `latest`. |\n| `traceId` | No | Exact trace ID. |\n| `sessionId` / `sessionKey` / `ovSessionId` | No | Session filters. |\n| `source` | No | `auto_recall`, `memory_recall`, `ov_search`, or `ov_archive_search`. |\n| `resourceTypes` | No | Target type filters: `resource`, `user`, `agent`. |\n| `since` / `until` | No | Unix timestamp bounds in milliseconds. |\n| `includeContent` | No | Read selected/displayed URI content previews on demand. |\n| `limit` | No | Maximum traces to return. Default: 20. |\n\nRequires `traceRecall=true`; persisted lookup also requires `traceRecallPersist=true`.\n\n### Externalized Tool Result Tools\n\nUse these when a preview contains a `viking://session/<session_id>/tool-results/<tool_result_id>` ref.\n\n| Tool | Parameters |\n|---|---|\n| `openviking_tool_result_list` | `tool_name?`, `limit?` (default 50) |\n| `openviking_tool_result_search` | `tool_output_ref`, `query`, `limit?` (default 20), `context_chars?` (default 300) |\n| `openviking_tool_result_read` | `tool_output_ref`, `offset?` (default 0), `limit?` (default 20000) |\n\n## Configuration Schema\n\nThese are the keys under `plugins.entries.openviking.config` in `openclaw.json`. The setup wizard / `ov-install` sets the first few; the rest are tunables.\n\n| Field | Default | Description |\n|---|---|---|\n| `mode` | `\"remote\"` (forced by plugin) | Always remote in this skill. Don't set manually. |\n| `baseUrl` | `http://127.0.0.1:1933` | OpenViking server URL. |\n| `apiKey` | — | API key. Optional if server has no auth. |\n| `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`. |\n| `peer_prefix` | `\"\"` | Optional prefix for assistant `peer_id` / actor peer values. Only used when `peer_role=assistant`. Letters / digits / `_` / `-`. |\n| `accountId` | — | Required when `apiKey` is a root key. |\n| `userId` | — | Required when `apiKey` is a root key. |\n| `targetUri` | `viking://~/memories` | Default search scope URI. |\n| `timeoutMs` | (plugin default) | HTTP timeout for OpenViking calls. |\n| `autoCapture` | `true` | Auto-append turn messages to the OpenViking session at `afterTurn`; extraction runs only after a threshold commit, `/compact`, or explicit `memory_store`. |\n| `captureMode` | `\"semantic\"` | Filter mode used by the server-side extraction pipeline: `semantic` or `keyword`. |\n| `captureMaxLength` | `24000` | Max text length per archived turn. |\n| `autoRecall` | `true` | Auto-recall and inject memories before reply. |\n| `recallTargetTypes` | `user,agent` | Default target types when `targetUri` is omitted. Allowed: `resource`, `user`, `agent`. |\n| `recallResources` | `false` | Compatibility shortcut that appends `resource` to default recall targets when `recallTargetTypes` is unset. |\n| `recallLimit` | `6` | Max memories injected per recall. |\n| `recallScoreThreshold` | `0.15` | Min relevance score to inject. |\n| `recallMaxInjectedChars` | `4000` | Hard cap on injected character count. Complete memories that do not fit are skipped, not truncated. |\n| `recallPreferAbstract` | (plugin default) | Prefer abstract memories over raw. |\n| `recallTokenBudget` | deprecated | Compatibility alias for `recallMaxInjectedChars`. |\n| `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. |\n| `commitKeepRecentCount` | `10` | Recent messages kept live after afterTurn commit. Compact always uses `0`. |\n| `bypassSessionPatterns` | — | Glob patterns for sessions skipped by capture. |\n| `ingestReplyAssist` | (plugin default) | Reply-assist ingestion toggle. |\n| `emitStandardDiagnostics` | `false` | Emit structured `openviking: diag {...}` lines. |\n| `logFindRequests` | `false` | Log routing for find/session writes. Also enabled by `OPENVIKING_LOG_ROUTING=1` or `OPENVIKING_DEBUG=1`. |\n| `traceRecall` | `false` | Record recall traces in memory. |\n| `traceRecallPersist` | `false` | Persist recall traces to JSONL files. |\n| `traceRecallDir` | `~/.openclaw/openviking/recall-traces` | Recall trace directory. |\n\nTo change a value:\n\n```bash\nopenclaw config set plugins.entries.openviking.config.<field> <value>\nopenclaw gateway restart\n```\n\n## Multi-Tenant (Root API Keys)\n\nSome 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:\n\n```\nRoot API key detected. Missing: --account-id, --user-id\n```\n\nWhen you see this:\n\n1. Ask the user for both values (they come from the OpenViking admin).\n2. Retry STEP 7 with both flags:\n\n```bash\nopenclaw openviking setup --base-url BASE_URL --api-key API_KEY --account-id ACCOUNT_ID --user-id USER_ID --json\n```\n\nA **user key** (issued per tenant) does not need these flags.\n\n## Multi-Instance (`--workdir` / `OPENCLAW_STATE_DIR`)\n\nIf the user runs multiple OpenClaw instances (e.g. testing several agents in parallel), each has its own state dir.\n\nTo target a non-default instance:\n\n```bash\nnpx -y openclaw-openviking-setup-helper@latest --workdir ~/.openclaw-second --base-url ... --api-key ...\n```\n\n`ov-install` writes a helper env file when the state dir is non-default:\n\n- Unix: `~/.openclaw/openviking.env` containing `export OPENCLAW_STATE_DIR='...'`\n- Windows: `~/.openclaw/openviking.env.bat` and `.ps1` setting the same variable\n\nSource it before running `openclaw` commands so they hit the correct state:\n\n**Unix:**\n```bash\nsource ~/.openclaw/openviking.env\nopenclaw status\n```\n\n**Windows (PowerShell):**\n```powershell\n. \"$HOME/.openclaw/openviking.env.ps1\"\nopenclaw status\n```\n\nOr pass `--workdir` directly to each `openclaw` invocation (note: not all `openclaw` subcommands honor `--workdir` consistently — when in doubt, prefer the env var).\n\n## Daily Operations\n\n```bash\n# Start or restart OpenClaw gateway after config changes\nopenclaw gateway restart\n\n# Check overall status\nopenclaw status\nopenclaw openviking status --json\n\n# Read current OpenViking slot\nopenclaw config get plugins.slots.contextEngine\n\n# Disable OpenViking memory (keep config, deactivate slot)\nopenclaw config set plugins.slots.contextEngine legacy\nopenclaw gateway restart\n\n# Re-enable\nopenclaw config set plugins.slots.contextEngine openviking\nopenclaw gateway restart\n```\n\n## Uninstall\n\n### Preferred: via OpenClaw plugin manager\n\n```bash\nopenclaw plugins uninstall openviking\nopenclaw config set plugins.slots.contextEngine legacy\nopenclaw gateway restart\n```\n\n### If installed through backup `ov-install`\n\n```bash\nnpx -y openclaw-openviking-setup-helper@latest --uninstall\n```\n\nThis will:\n\n1. Stop the OpenClaw gateway.\n2. Back up `openclaw.json`.\n3. Remove all OpenViking plugin config from `openclaw.json` (`plugins.entries.openviking`, `plugins.allow`, `plugins.installs.openviking`, `plugins.slots.contextEngine`).\n4. Move the plugin directory to `disabled-extensions/` as backup.\n5. Remove the helper env files (`openviking.env`, `.bat`, `.ps1`).\n\nAdd `--base-url dummy` for fully non-interactive mode (skips the confirmation prompt).\n\n### Manual\n\n```bash\nopenclaw gateway stop\nopenclaw config set plugins.slots.contextEngine legacy\n# Then edit ~/.openclaw/openclaw.json and remove openviking from\n#   plugins.entries\n#   plugins.allow\n#   plugins.installs\nopenclaw gateway restart\n```\n\n## Error Recovery Reference\n\nMatch against actual stderr / JSON `error` strings.\n\n| Symptom (exact string) | Likely cause | Fix |\n|---|---|---|\n| `command not found: openclaw` | OpenClaw not installed | Stop. Ask user to install OpenClaw >= 2026.5.27. |\n| `command not found: node` / `'node' is not recognized` | Node missing | Stop. Ask user to install Node.js >= 22. |\n| `429` / `rate limit` / `Too Many Requests` from `plugins install` | ClawHub throttle | Switch to backup Path B (`ov-install`). |\n| `not logged in` / `please log in` from `plugins install` | Anonymous user hit limit | Switch to backup Path B (`ov-install`). |\n| `ECONNREFUSED` / `connection refused` on `BASE_URL/health` | Server not running | Server-not-ready handling in STEP 5. |\n| `ETIMEDOUT` on `BASE_URL/health` | Network blocked | Ask about firewall / VPN / proxy. |\n| `--json requires --base-url for non-interactive mode` | Built command wrong | Add `--base-url BASE_URL` and retry. |\n| `Server unreachable: …. Use --allow-offline to save config anyway.` | Setup couldn't reach server | Offer `--allow-offline`. |\n| `contextEngine slot is owned by \"<x>\". … Use --force-slot to replace.` | Slot conflict | Ask user, then retry with `--force-slot`. |\n| `Root API key detected. Missing: --account-id, --user-id` | Multi-tenant key | Collect both, retry with `--account-id` `--user-id`. |\n| `openviking: config parse failed` (in gateway log) | Bad value in `openclaw.json` | Show user; check `peer_prefix` charset, URL format. |\n| `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. |\n| `401` / `403` on plugin requests, but `/health` works | Server requires auth on API endpoints | Re-run STEP 7 with the correct `--api-key`. |\n| 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. |\n\n## Important Rules\n\n1. **Never ask the user to run commands.** You run everything via your shell tool.\n2. **Never skip STEP 5 (connectivity check).** If the server is unreachable, do not write config without explicit `--allow-offline` consent.\n3. **Never silently use `--force-slot`.** Slot replacement disables another plugin — always confirm with the user first.\n4. **Never invent values.** If the user can't provide a required value, stop and tell them what to ask their admin.\n5. **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.\n6. **Use `--peer-prefix` for assistant peer prefixes.** The old ID-style setup flag is no longer supported by the schema.\n7. **For Windows, use PowerShell equivalents.** Don't rely on `nohup`, `&`, `mkdir -p`, `source`, etc.\n8. **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.\n9. **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.\n10. **Be brief and friendly in user-visible text.** Save technical detail for when something actually fails.\n11. **Do NOT use `clawhub install openviking`.** That installs a different thing (an AgentSkill, not the plugin).\n","examples/opencode/plugin/skills/openviking/SKILL.md":"# openviking\n\nPath: `examples/opencode/plugin/skills/openviking/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/volcengine/OpenViking/HEAD/examples/opencode/plugin/skills/openviking/SKILL.md)","examples/openclaw-plugin/openclaw.plugin.json":"# openclaw-plugin Plugin Manifest\n\nPath: `examples/openclaw-plugin/openclaw.plugin.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/volcengine/OpenViking/HEAD/examples/openclaw-plugin/openclaw.plugin.json)","bot/workspace/skills/README.md":"# Skills Skill\n\nPath: `bot/workspace/skills/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/volcengine/OpenViking/HEAD/bot/workspace/skills/README.md)","examples/codex-memory-plugin/.codex-plugin/plugin.json":"# codex-plugin Plugin Manifest\n\nPath: `examples/codex-memory-plugin/.codex-plugin/plugin.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/volcengine/OpenViking/HEAD/examples/codex-memory-plugin/.codex-plugin/plugin.json)","examples/claude-code-memory-plugin/.claude-plugin/plugin.json":"# claude-plugin Plugin Manifest\n\nPath: `examples/claude-code-memory-plugin/.claude-plugin/plugin.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/volcengine/OpenViking/HEAD/examples/claude-code-memory-plugin/.claude-plugin/plugin.json)","bot/workspace/skills/opencode/status.json":"# Opencode Skill\n\nPath: `bot/workspace/skills/opencode/status.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/volcengine/OpenViking/HEAD/bot/workspace/skills/opencode/status.json)"},"files":{"bot/workspace/skills/github/SKILL.md":"---\nname: github\ndescription: \"Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.\"\nmetadata: {\"vikingbot\":{\"emoji\":\"🐙\",\"requires\":{\"bins\":[\"gh\"]},\"install\":[{\"id\":\"brew\",\"kind\":\"brew\",\"formula\":\"gh\",\"bins\":[\"gh\"],\"label\":\"Install GitHub CLI (brew)\"},{\"id\":\"apt\",\"kind\":\"apt\",\"package\":\"gh\",\"bins\":[\"gh\"],\"label\":\"Install GitHub CLI (apt)\"}]}}\n---\n\n# GitHub Skill\n\nUse the `gh` CLI to interact with GitHub. Always specify `--repo owner/repo` when not in a git directory, or use URLs directly.\n\n## Pull Requests\n\nCheck CI status on a PR:\n```bash\ngh pr checks 55 --repo owner/repo\n```\n\nList recent workflow runs:\n```bash\ngh run list --repo owner/repo --limit 10\n```\n\nView a run and see which steps failed:\n```bash\ngh run view <run-id> --repo owner/repo\n```\n\nView logs for failed steps only:\n```bash\ngh run view <run-id> --repo owner/repo --log-failed\n```\n\n## API for Advanced Queries\n\nThe `gh api` command is useful for accessing data not available through other subcommands.\n\nGet PR with specific fields:\n```bash\ngh api repos/owner/repo/pulls/55 --jq '.title, .state, .user.login'\n```\n\n## JSON Output\n\nMost commands support `--json` for structured output.  You can use `--jq` to filter:\n\n```bash\ngh issue list --repo owner/repo --json number,title --jq '.[] | \"\\(.number): \\(.title)\"'\n```\n","bot/workspace/skills/github-proxy/SKILL.md":"---\nname: github-proxy\ndescription: GitHub 国内访问加速 skill，使用 githubproxy.cc 代理加速 GitHub 仓库克隆、文件下载、Raw 文件访问等操作。使用场景：(1) 需要 git clone GitHub 仓库时加速，(2) 下载 GitHub Release 文件、Raw 文件、Archive 压缩包时加速，(3) 任何需要访问 GitHub 资源但速度慢的场景\n---\n\n# GitHub 国内代理加速 Skill\n\n使用 githubproxy.cc 代理服务，为国内访问 GitHub 提供加速支持。\n\n## 代理服务\n\n当前使用的代理服务：\n- **主要服务**: githubproxy.cc (测试有效，加速约 3 倍)\n- **备用服务**: ghfast.top\n\n## 使用方法\n\n### 1. Git Clone 加速\n\n将 GitHub 仓库链接前加上 `https://githubproxy.cc/` 前缀：\n\n```bash\n# 原始链接\ngit clone https://github.com/username/repo.git\n\n# 加速链接\ngit clone https://githubproxy.cc/https://github.com/username/repo.git\n```\n\n### 2. 文件下载加速\n\n支持以下类型的 GitHub 资源加速：\n\n- **Raw 文件**: `https://raw.githubusercontent.com/...`\n- **Release 文件**: 项目发布的附件\n- **Archive 压缩包**: 仓库打包下载\n- **Gist 文件**: `gist.github.com` 或 `gist.githubusercontent.com`\n\n```bash\n# 原始链接\nwget https://raw.githubusercontent.com/username/repo/main/file.txt\n\n# 加速链接\nwget https://githubproxy.cc/https://raw.githubusercontent.com/username/repo/main/file.txt\n```\n\n### 3. 使用辅助脚本\n\n使用 `scripts/convert_url.py` 自动转换 GitHub 链接：\n\n```bash\npython scripts/convert_url.py \"https://github.com/username/repo.git\"\n```\n\n## 链接转换规则\n\n| 原始链接格式 | 转换后格式 |\n|-------------|-----------|\n| `https://github.com/username/repo.git` | `https://githubproxy.cc/https://github.com/username/repo.git` |\n| `https://raw.githubusercontent.com/...` | `https://githubproxy.cc/https://raw.githubusercontent.com/...` |\n| `https://github.com/.../releases/download/...` | `https://githubproxy.cc/https://github.com/.../releases/download/...` |\n| `https://github.com/.../archive/...` | `https://githubproxy.cc/https://github.com/.../archive/...` |\n\n## 注意事项\n\n- 本服务仅供学习研究使用，请勿滥用\n- 如果 githubproxy.cc 不可用，请尝试备用服务 ghfast.top\n- 不支持 SSH Key 方式的 git clone\n- Push、PR、Issue 等操作建议直接使用官方 GitHub 地址\n","bot/workspace/skills/opencode/SKILL.md":"---\nname: opencode\ndescription: Monitor and manage your OpenCode tasks using helper scripts.\nmetadata: {\"vikingbot\":{\"emoji\":\"💻\",\"requires\":{\"bins\":[\"python3\"]}}}\n---\n\n# OpenCode Skill\n\nUse helper scripts to manage your OpenCode instances.\n\n## Helper Scripts\n\nAll scripts are in the workspace skills directory. Run them with:\n```bash\nuv run python skills/opencode/script_name.py\n```\n\nNote: Do not kill the opencode process.\n\n## Scripts\n\n### `list_sessions.py` \nListing all OpenCode sessions\n\nExample:\nuv run python skills/opencode/list_sessions.py\n\n\n### `list_messages_of_session.py`\nListing latest OpenCode messages by session_id \n\nExample:\nuv run python skills/opencode/list_sessions.py {session_id}\n\n## Session Status Types\n\n- **🟢 WAITING**: Last message was from user - agent is waiting for input\n- **🔴 WORKING**: Last message was from assistant - agent recently finished or may be working\n- **🟡 UNKNOWN**: Cannot determine status\n\n","examples/skills/ov-server-operate/SKILL.md":"---\nname: ov-server-operate\ndescription: Operate and maintain OpenViking server - configure, install, start, stop, and cleanup the server. Use when need to setup or manage OpenViking service deployment.\ncompatibility: OpenViking CLI configured at `~/.openviking/ovcli.conf`\n---\n\n# OpenViking Server Operations\n\nThis guide provides standard operating procedures for deploying, managing, and maintaining OpenViking servers in production environments.\n\n## Table of Content\n- Service Configuration\n- Environment Setup with uv\n- Server Startup with nohup\n- Server Shutdown\n- Data Cleanup Procedure\n- Verification and Troubleshooting\n\n## Service Configuration\n\n### Default Paths and Structure\n\nOpenViking uses the following standard directory structure under `~/.openviking/`:\n\n```\n~/.openviking/\n├── ov.conf             # Server configuration (required)\n├── ovcli.conf          # CLI client configuration\n├── ov-venv/            # Virtual environment (created by uv)\n├── log/                # Server log directory\n│   ├── openviking-server.log   # server stdout log\n│   └── openviking.log          # server log\n└── data/               # Workspace data (configured in ov.conf)\n    ├── ...\n    └── ...\n```\n\n### Configuration Files\n\n#### 1. Server Config (`~/.openviking/ov.conf`)\n\nCreate the configuration file with at minimum the following configuration.\nNote 1: Replace the api-key with your own api-key. If you don't have one, ask the user to get one (follow the Volcengine Ark platform guide).\nNote 2: Replace the root_api_key with your own root-api-key. Ask the user to set one — it will be used for authentication when the CLI connects to the server.\n\n```json\n{\n  \"server\": {\n    \"host\": \"0.0.0.0\",\n    \"port\": 1933,\n    \"root_api_key\": \"your-root-api-key\"\n  },\n  \"storage\": {\n    \"workspace\": \"~/.openviking/data/\"\n  },\n  \"parsers\": {\n    \"code\": {\n      \"gitlab_domains\": [\"code.byted.org\"],\n      \"azure_devops_domains\": [\"ssh.dev.azure.com\", \"vs-ssh.visualstudio.com\"]\n    }\n  },\n  \"embedding\": {\n    \"dense\": {\n        \"model\": \"doubao-embedding-vision-251215\",\n        \"api_key\": \"your-volcengine-api-key\",\n        \"api_base\": \"https://ark.cn-beijing.volces.com/api/v3\",\n        \"dimension\": 1024,\n        \"input\": \"multimodal\",\n        \"provider\": \"volcengine\"\n    }\n  },\n  \"vlm\": {\n    \"model\": \"doubao-seed-1-8-251228\",\n    \"api_key\": \"your-volcengine-api-key\",\n    \"api_base\": \"https://ark.cn-beijing.volces.com/api/v3\",\n    \"temperature\": 0.0,\n    \"max_retries\": 2,\n    \"provider\": \"volcengine\",\n    \"thinking\": false\n  },\n  \"log\": {\n    \"level\": \"INFO\",\n    \"output\": \"file\",\n    \"rotation\": true,\n    \"rotation_days\": 3,\n    \"rotation_interval\": \"midnight\"\n  }\n}\n```\n\n#### 2. CLI Config (`~/.openviking/ovcli.conf`)\n\nFor client connections from localhost:\n\n```json\n{\n  \"url\": \"http://localhost:1933\",\n  \"api_key\": \"your-root-api-key\"\n}\n```\n\nFor remote connections, set the url to the remote server address (for example, the server EIP).\n\n## Environment Setup with uv\n\n### Step 1: Install uv (if not already installed)\n\n```bash\n# macOS/Linux\ncurl -LsSf https://astral.sh/uv/install.sh | sh\n\n# Verify installation\nuv --version\n```\n\n### Step 2: Create Virtual Environment\n\nCreate a dedicated virtual environment at `~/.openviking/ov-venv`:\n\n```bash\n# Create venv with Python 3.10+\ncd ~/.openviking\nuv venv --python 3.12 ov-venv\n```\n\n### Step 3: Activate and Install OpenViking\n\n```bash\n# Activate the virtual environment\nsource ~/.openviking/ov-venv/bin/activate\n\n# Install or upgrade to latest openviking\nuv pip install --upgrade openviking --force-reinstall\n\n# Verify installation\nwhich openviking-server\nopenviking-server --version\nopenviking-server --help\n```\n\n### Step 4: Create Log Directory\n\n```bash\nmkdir -p ~/.openviking/log\n```\n\n## Server Startup with nohup\n\n### Standard Startup Procedure\n\n```bash\n# 1. Activate the virtual environment\nsource ~/.openviking/ov-venv/bin/activate\n\n# 2. Ensure log directory exists\nmkdir -p ~/.openviking/log\n\n# 3. Start server with nohup\nnohup openviking-server \\\n    > ~/.openviking/log/openviking-server.log 2>&1 &\n\n# 4. Save PID for later reference\necho $! > ~/.openviking/server.pid\n\n# 5. Verify startup after 10 secs\nsleep 10\ncurl -s http://localhost:1933/health\n```\n\n### Verify Server is Running\n\n```bash\n# Method 1: Check health endpoint\ncurl http://localhost:1933/health\n# Expected: {\"status\": \"ok\"}\n\n# Method 2: Check readiness (includes storage checks)\ncurl http://localhost:1933/ready\n\n# Method 3: Check process\nps aux | grep openviking-server | grep -v grep\n\n# Method 4: Check log output\ntail -10 ~/.openviking/log/openviking-server.log\ntail -50 ~/.openviking/log/openviking.log\n```\n\n## Server Shutdown\n\n### Graceful Shutdown Procedure\n\n```bash\n# 1. Find the server process\nps aux | grep openviking-server | grep -v grep\n\n# 2. Send SIGTERM for graceful shutdown\n# Option A: Using saved PID\nif [ -f ~/.openviking/server.pid ]; then\n    kill $(cat ~/.openviking/server.pid)\n    rm ~/.openviking/server.pid\nfi\n\n# Option B: Using pgrep\npkill -f openviking-server\n\n# 3. Wait for process to stop\nsleep 3\n\n# 4. Verify it stopped\nps aux | grep openviking-server | grep -v grep || echo \"Server stopped successfully\"\n\n# 5. If still running, force kill\nif pgrep -f openviking-server > /dev/null; then\n    echo \"Force killing server...\"\n    pkill -9 -f openviking-server\nfi\n```\n\n## Data Cleanup Procedure\n\n### When to Use This Procedure\n\nPerform full data cleanup in these scenarios:\n1. Version upgrade with incompatible data format\n2. Corrupted or inconsistent data\n3. Need to reset to fresh state\n4. Storage space reclamation\n\n### Standard Cleanup Workflow\n\n**CRITICAL: ALWAYS BACKUP BEFORE DELETING DATA**\n\n```bash\n# ==========================================\n# STEP 1: STOP THE SERVER FIRST\n# ==========================================\necho \"Step 1: Stopping OpenViking Server...\"\nif pgrep -f openviking-server > /dev/null; then\n    pkill -f openviking-server\n    sleep 3\n    if pgrep -f openviking-server > /dev/null; then\n        pkill -9 -f openviking-server\n        sleep 1\n    fi\nfi\n\n# Verify server is stopped\nif pgrep -f openviking-server > /dev/null; then\n    echo \"ERROR: Server still running! Cannot proceed.\"\n    exit 1\nfi\necho \"✓ Server stopped\"\n\n# ==========================================\n# STEP 2: CREATE BACKUP (REQUIRED)\n# ==========================================\necho \"\"\necho \"Step 2: Creating backup...\"\nBACKUP_DATE=$(date +%Y%m%d_%H%M%S)\nBACKUP_DIR=~/.openviking/backup_${BACKUP_DATE}\n\nmkdir -p ${BACKUP_DIR}\n\n# Backup config files\ncp ~/.openviking/ov.conf ${BACKUP_DIR}/ 2>/dev/null || true\ncp ~/.openviking/ovcli.conf ${BACKUP_DIR}/ 2>/dev/null || true\n\n# Backup workspace (if exists)\nWORKSPACE=$(python3 -c '\nimport json\nimport os\nconfig_path = os.path.expanduser(\"~/.openviking/ov.conf\")\nif os.path.exists(config_path):\n    with open(config_path) as f:\n        cfg = json.load(f)\n        ws = cfg.get(\"storage\", {}).get(\"workspace\", \"./data\")\n        print(os.path.expanduser(ws))\n' 2>/dev/null || echo \"~/.openviking/data\")\n\nif [ -d \"${WORKSPACE}\" ]; then\n    echo \"Backing up workspace: ${WORKSPACE}\"\n    tar -czf ${BACKUP_DIR}/workspace_backup.tar.gz -C $(dirname ${WORKSPACE}) $(basename ${WORKSPACE})\nfi\n\n# Backup log\nif [ -d ~/.openviking/log ]; then\n    cp -r ~/.openviking/log ${BACKUP_DIR}/ 2>/dev/null || true\nfi\n\necho \"✓ Backup created at: ${BACKUP_DIR}\"\nls -lh ${BACKUP_DIR}/\n\n# ==========================================\n# STEP 3: CONFIRM DELETION\n# ==========================================\necho \"\"\necho \"==========================================\"\necho \"WARNING: ABOUT TO DELETE ALL DATA!\"\necho \"==========================================\"\necho \"Workspace to delete: ${WORKSPACE}\"\necho \"Backup location: ${BACKUP_DIR}\"\necho \"\"\nread -p \"Type 'DELETE' to confirm data removal: \" CONFIRM\n\nif [ \"${CONFIRM}\" != \"DELETE\" ]; then\n    echo \"Cleanup cancelled. Backup preserved at ${BACKUP_DIR}\"\n    exit 0\nfi\n\n# ==========================================\n# STEP 4: DELETE DATA\n# ==========================================\necho \"\"\necho \"Step 4: Deleting data...\"\n\n# Delete workspace\nif [ -d \"${WORKSPACE}\" ]; then\n    echo \"Deleting workspace: ${WORKSPACE}\"\n    rm -rf \"${WORKSPACE}\"\nfi\n\n# Optional: Delete old log (uncomment if needed)\n# echo \"Deleting old log...\"\n# rm -rf ~/.openviking/log/*\n\n# Cleanup any temporary files\nrm -f ~/.openviking/server.pid\n\necho \"✓ Data deleted successfully\"\n\n# ==========================================\n# STEP 5: COMPLETION\n# ==========================================\necho \"\"\necho \"==========================================\"\necho \"Cleanup Complete!\"\necho \"==========================================\"\necho \"Backup preserved at: ${BACKUP_DIR}\"\necho \"\"\necho \"Next steps:\"\necho \"1. Reconfigure ov.conf if needed\"\necho \"2. Start the server: openviking-server\"\necho \"3. Verify with: curl http://localhost:1933/health\"\necho \"\"\necho \"To restore from backup:\"\necho \"  tar -xzf ${BACKUP_DIR}/workspace_backup.tar.gz -C $(dirname ${WORKSPACE})\"\n```\n\n### Quick Cleanup (for Development Only)\n\n```bash\n# WARNING: Only use in development!\n# No backup created - data loss guaranteed!\n\n# 1. Stop server\npkill -f openviking-server\nsleep 2\npkill -9 -f openviking-server 2>/dev/null || true\n\n# 2. Delete workspace (adjust path as needed)\nrm -rf ~/.openviking/data\n\n# 3. Cleanup PID and temp files\nrm -f ~/.openviking/server.pid\n\necho \"Quick cleanup complete\"\n```\n\n## Verification and Troubleshooting\n\n### Health Check Verification\n\n```bash\n# Basic health check (always available)\ncurl http://localhost:1933/health\n# Expected: {\"status\": \"ok\"}\n\n# Readiness check (verifies all components)\ncurl http://localhost:1933/ready\n# Expected: {\"status\": \"ready\", \"checks\": {\"agfs\": \"ok\", \"vectordb\": \"ok\", \"api_key_manager\": \"ok\"}}\n\n# System status via CLI (~/.openviking/ovcli.conf should be configured)\nov status\n```\n\n### Common Issues and Solutions\n\n#### Issue: Server won't start\n\n**Check:**\n```bash\n# 1. Check if port is in use\nlsof -i :1933\nnetstat -tulpn | grep 1933\n\n# 2. Check log for errors\ntail -10 ~/.openviking/log/openviking-server.log\ntail -100 ~/.openviking/log/openviking.log\n\n\n# 3. Verify config file is valid JSON\npython3 -c 'import json, os; json.load(open(os.path.expanduser(\"~/.openviking/ov.conf\"))); print(\"Config is valid\")'\n\n# 4. Verify virtual environment\nsource ~/.openviking/ov-venv/bin/activate\nwhich openviking-server\npip list | grep openviking\n```\n\n**Solution:**\n```bash\n# If port conflict: kill the process or use different port\nlsof -ti :1933 | xargs kill -9 2>/dev/null || true\n\n# Or start on different port\nnohup openviking-server --port 1934 > ~/.openviking/log/openviking-server.log 2>&1 &\n```\n\n#### Issue: API Key Errors\n\n**Check:**\n```bash\n# Verify API keys in config\npython3 -c '\nimport json, os\ncfg = json.load(open(os.path.expanduser(\"~/.openviking/ov.conf\")))\nprint(\"Embedding provider:\", cfg.get(\"embedding\", {}).get(\"dense\", {}).get(\"provider\"))\nprint(\"VLM provider:\", cfg.get(\"vlm\", {}).get(\"provider\"))\nprint(\"API keys set:\", bool(cfg.get(\"embedding\", {}).get(\"dense\", {}).get(\"api_key\")), bool(cfg.get(\"vlm\", {}).get(\"api_key\")))\n'\n```\n\n**Solution:** Verify API keys are correct and have the required permissions. Check network connectivity to the model provider endpoints. Ensure API keys are not expired.\n\n## Prerequisites\n\n- Python 3.10+ installed\n- uv package manager available\n- Sufficient disk space for workspace and log\n- API keys for embedding and VLM models configured\n- Network access to model providers (if using cloud models)\n","bot/workspace/skills/summarize/SKILL.md":"---\nname: summarize\ndescription: Summarize or extract text/transcripts from URLs, podcasts, and local files (great fallback for “transcribe this YouTube/video”).\nhomepage: https://summarize.sh\nmetadata: {\"vikingbot\":{\"emoji\":\"🧾\",\"requires\":{\"bins\":[\"summarize\"]},\"install\":[{\"id\":\"brew\",\"kind\":\"brew\",\"formula\":\"steipete/tap/summarize\",\"bins\":[\"summarize\"],\"label\":\"Install summarize (brew)\"}]}}\n---\n\n# Summarize\n\nFast CLI to summarize URLs, local files, and YouTube links.\n\n## When to use (trigger phrases)\n\nUse this skill immediately when the user asks any of:\n- “use summarize.sh”\n- “what’s this link/video about?”\n- “summarize this URL/article”\n- “transcribe this YouTube/video” (best-effort transcript extraction; no `yt-dlp` needed)\n\n## Quick start\n\n```bash\nsummarize \"https://example.com\" --model google/gemini-3-flash-preview\nsummarize \"/path/to/file.pdf\" --model google/gemini-3-flash-preview\nsummarize \"https://youtu.be/dQw4w9WgXcQ\" --youtube auto\n```\n\n## YouTube: summary vs transcript\n\nBest-effort transcript (URLs only):\n\n```bash\nsummarize \"https://youtu.be/dQw4w9WgXcQ\" --youtube auto --extract-only\n```\n\nIf the user asked for a transcript but it’s huge, return a tight summary first, then ask which section/time range to expand.\n\n## Model + keys\n\nSet the API key for your chosen provider:\n- OpenAI: `OPENAI_API_KEY`\n- Anthropic: `ANTHROPIC_API_KEY`\n- xAI: `XAI_API_KEY`\n- Google: `GEMINI_API_KEY` (aliases: `GOOGLE_GENERATIVE_AI_API_KEY`, `GOOGLE_API_KEY`)\n\nDefault model is `google/gemini-3-flash-preview` if none is set.\n\n## Useful flags\n\n- `--length short|medium|long|xl|xxl|<chars>`\n- `--max-output-tokens <count>`\n- `--extract-only` (URLs only)\n- `--json` (machine readable)\n- `--firecrawl auto|off|always` (fallback extraction)\n- `--youtube auto` (Apify fallback if `APIFY_API_TOKEN` set)\n\n## Config\n\nOptional config file: `~/.summarize/config.json`\n\n```json\n{ \"model\": \"openai/gpt-5.2\" }\n```\n\nOptional services:\n- `FIRECRAWL_API_KEY` for blocked sites\n- `APIFY_API_TOKEN` for YouTube fallback\n","bot/workspace/skills/cron/SKILL.md":"---\nname: cron\ndescription: Schedule reminders and recurring tasks.\n---\n\n# Cron\n\nUse the `cron` tool to schedule reminders or recurring tasks.\n\n## Three Modes\n\n1. **Reminder** - message is sent directly to user\n2. **Task** - message is a task description, agent executes and sends result\n3. **One-time** - runs once at a specific time, then auto-deletes\n\n## Examples\n\nFixed reminder:\n```\ncron(action=\"add\", message=\"Time to take a break!\", every_seconds=1200)\n```\n\nDynamic task (agent executes each time):\n```\ncron(action=\"add\", message=\"Check HKUDS/vikingbot GitHub stars and report\", every_seconds=600)\n```\n\nOne-time scheduled task (compute ISO datetime from current time):\n```\ncron(action=\"add\", message=\"Remind me about the meeting\", at=\"<ISO datetime>\")\n```\n\nList/remove:\n```\ncron(action=\"list\")\ncron(action=\"remove\", job_id=\"abc123\")\n```\n\n## Time Expressions\n\n| User says | Parameters |\n|-----------|------------|\n| every 20 minutes | every_seconds: 1200 |\n| every hour | every_seconds: 3600 |\n| every day at 8am | cron_expr: \"0 8 * * *\" |\n| weekdays at 5pm | cron_expr: \"0 17 * * 1-5\" |\n| at a specific time | at: ISO datetime string (compute from current time) |\n","bot/workspace/skills/tmux/SKILL.md":"---\nname: tmux\ndescription: Remote-control tmux sessions for interactive CLIs by sending keystrokes and scraping pane output.\nmetadata: {\"vikingbot\":{\"emoji\":\"🧵\",\"os\":[\"darwin\",\"linux\"],\"requires\":{\"bins\":[\"tmux\"]}}}\n---\n\n# tmux Skill\n\nUse tmux only when you need an interactive TTY. Prefer exec background mode for long-running, non-interactive tasks.\n\n## Quickstart (isolated socket, exec tool)\n\n```bash\nSOCKET_DIR=\"${NANOBOT_TMUX_SOCKET_DIR:-${TMPDIR:-/tmp}/vikingbot-tmux-sockets}\"\nmkdir -p \"$SOCKET_DIR\"\nSOCKET=\"$SOCKET_DIR/vikingbot.sock\"\nSESSION=vikingbot-python\n\ntmux -S \"$SOCKET\" new -d -s \"$SESSION\" -n shell\ntmux -S \"$SOCKET\" send-keys -t \"$SESSION\":0.0 -- 'PYTHON_BASIC_REPL=1 python3 -q' Enter\ntmux -S \"$SOCKET\" capture-pane -p -J -t \"$SESSION\":0.0 -S -200\n```\n\nAfter starting a session, always print monitor commands:\n\n```\nTo monitor:\n  tmux -S \"$SOCKET\" attach -t \"$SESSION\"\n  tmux -S \"$SOCKET\" capture-pane -p -J -t \"$SESSION\":0.0 -S -200\n```\n\n## Socket convention\n\n- Use `NANOBOT_TMUX_SOCKET_DIR` environment variable.\n- Default socket path: `\"$NANOBOT_TMUX_SOCKET_DIR/vikingbot.sock\"`.\n\n## Targeting panes and naming\n\n- Target format: `session:window.pane` (defaults to `:0.0`).\n- Keep names short; avoid spaces.\n- Inspect: `tmux -S \"$SOCKET\" list-sessions`, `tmux -S \"$SOCKET\" list-panes -a`.\n\n## Finding sessions\n\n- List sessions on your socket: `{baseDir}/scripts/find-sessions.sh -S \"$SOCKET\"`.\n- Scan all sockets: `{baseDir}/scripts/find-sessions.sh --all` (uses `NANOBOT_TMUX_SOCKET_DIR`).\n\n## Sending input safely\n\n- Prefer literal sends: `tmux -S \"$SOCKET\" send-keys -t target -l -- \"$cmd\"`.\n- Control keys: `tmux -S \"$SOCKET\" send-keys -t target C-c`.\n\n## Watching output\n\n- Capture recent history: `tmux -S \"$SOCKET\" capture-pane -p -J -t target -S -200`.\n- Wait for prompts: `{baseDir}/scripts/wait-for-text.sh -t session:0.0 -p 'pattern'`.\n- Attaching is OK; detach with `Ctrl+b d`.\n\n## Spawning processes\n\n- For python REPLs, set `PYTHON_BASIC_REPL=1` (non-basic REPL breaks send-keys flows).\n\n## Windows / WSL\n\n- tmux is supported on macOS/Linux. On Windows, use WSL and install tmux inside WSL.\n- This skill is gated to `darwin`/`linux` and requires `tmux` on PATH.\n\n## Orchestrating Coding Agents (Codex, Claude Code)\n\ntmux excels at running multiple coding agents in parallel:\n\n```bash\nSOCKET=\"${TMPDIR:-/tmp}/codex-army.sock\"\n\n# Create multiple sessions\nfor i in 1 2 3 4 5; do\n  tmux -S \"$SOCKET\" new-session -d -s \"agent-$i\"\ndone\n\n# Launch agents in different workdirs\ntmux -S \"$SOCKET\" send-keys -t agent-1 \"cd /tmp/project1 && codex --yolo 'Fix bug X'\" Enter\ntmux -S \"$SOCKET\" send-keys -t agent-2 \"cd /tmp/project2 && codex --yolo 'Fix bug Y'\" Enter\n\n# Poll for completion (check if prompt returned)\nfor sess in agent-1 agent-2; do\n  if tmux -S \"$SOCKET\" capture-pane -p -t \"$sess\" -S -3 | grep -q \"❯\"; then\n    echo \"$sess: DONE\"\n  else\n    echo \"$sess: Running...\"\n  fi\ndone\n\n# Get full output from completed session\ntmux -S \"$SOCKET\" capture-pane -p -t agent-1 -S -500\n```\n\n**Tips:**\n- Use separate git worktrees for parallel fixes (no branch conflicts)\n- `pnpm install` first before running codex in fresh clones\n- Check for shell prompt (`❯` or `$`) to detect completion\n- Codex needs `--yolo` or `--full-auto` for non-interactive fixes\n\n## Cleanup\n\n- Kill a session: `tmux -S \"$SOCKET\" kill-session -t \"$SESSION\"`.\n- Kill all sessions on a socket: `tmux -S \"$SOCKET\" list-sessions -F '#{session_name}' | xargs -r -n1 tmux -S \"$SOCKET\" kill-session -t`.\n- Remove everything on the private socket: `tmux -S \"$SOCKET\" kill-server`.\n\n## Helper: wait-for-text.sh\n\n`{baseDir}/scripts/wait-for-text.sh` polls a pane for a regex (or fixed string) with a timeout.\n\n```bash\n{baseDir}/scripts/wait-for-text.sh -t session:0.0 -p 'pattern' [-F] [-T 20] [-i 0.5] [-l 2000]\n```\n\n- `-t`/`--target` pane target (required)\n- `-p`/`--pattern` regex to match (required); add `-F` for fixed string\n- `-T` timeout seconds (integer, default 15)\n- `-i` poll interval seconds (default 0.5)\n- `-l` history lines to search (integer, default 1000)\n","bot/workspace/skills/skill-creator/SKILL.md":"---\nname: skill-creator\ndescription: Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.\n---\n\n# Skill Creator\n\nThis skill provides guidance for creating effective skills.\n\n## About Skills\n\nSkills are modular, self-contained packages that extend the agent's capabilities by providing\nspecialized knowledge, workflows, and tools. Think of them as \"onboarding guides\" for specific\ndomains or tasks—they transform the agent from a general-purpose agent into a specialized agent\nequipped with procedural knowledge that no model can fully possess.\n\n### What Skills Provide\n\n1. Specialized workflows - Multi-step procedures for specific domains\n2. Tool integrations - Instructions for working with specific file formats or APIs\n3. Domain expertise - Company-specific knowledge, schemas, business logic\n4. Bundled resources - Scripts, references, and assets for complex and repetitive tasks\n\n## Core Principles\n\n### Concise is Key\n\nThe context window is a public good. Skills share the context window with everything else the agent needs: system prompt, conversation history, other Skills' metadata, and the actual user request.\n\n**Default assumption: the agent is already very smart.** Only add context the agent doesn't already have. Challenge each piece of information: \"Does the agent really need this explanation?\" and \"Does this paragraph justify its token cost?\"\n\nPrefer concise examples over verbose explanations.\n\n### Set Appropriate Degrees of Freedom\n\nMatch the level of specificity to the task's fragility and variability:\n\n**High freedom (text-based instructions)**: Use when multiple approaches are valid, decisions depend on context, or heuristics guide the approach.\n\n**Medium freedom (pseudocode or scripts with parameters)**: Use when a preferred pattern exists, some variation is acceptable, or configuration affects behavior.\n\n**Low freedom (specific scripts, few parameters)**: Use when operations are fragile and error-prone, consistency is critical, or a specific sequence must be followed.\n\nThink of the agent as exploring a path: a narrow bridge with cliffs needs specific guardrails (low freedom), while an open field allows many routes (high freedom).\n\n### Anatomy of a Skill\n\nEvery skill consists of a required SKILL.md file and optional bundled resources:\n\n```\nskill-name/\n├── SKILL.md (required)\n│   ├── YAML frontmatter metadata (required)\n│   │   ├── name: (required)\n│   │   └── description: (required)\n│   └── Markdown instructions (required)\n└── Bundled Resources (optional)\n    ├── scripts/          - Executable code (Python/Bash/etc.)\n    ├── references/       - Documentation intended to be loaded into context as needed\n    └── assets/           - Files used in output (templates, icons, fonts, etc.)\n```\n\n#### SKILL.md (required)\n\nEvery SKILL.md consists of:\n\n- **Frontmatter** (YAML): Contains `name` and `description` fields. These are the only fields that the agent reads to determine when the skill gets used, thus it is very important to be clear and comprehensive in describing what the skill is, and when it should be used.\n- **Body** (Markdown): Instructions and guidance for using the skill. Only loaded AFTER the skill triggers (if at all).\n\n#### Bundled Resources (optional)\n\n##### Scripts (`scripts/`)\n\nExecutable code (Python/Bash/etc.) for tasks that require deterministic reliability or are repeatedly rewritten.\n\n- **When to include**: When the same code is being rewritten repeatedly or deterministic reliability is needed\n- **Example**: `scripts/rotate_pdf.py` for PDF rotation tasks\n- **Benefits**: Token efficient, deterministic, may be executed without loading into context\n- **Note**: Scripts may still need to be read by the agent for patching or environment-specific adjustments\n\n##### References (`references/`)\n\nDocumentation and reference material intended to be loaded as needed into context to inform the agent's process and thinking.\n\n- **When to include**: For documentation that the agent should reference while working\n- **Examples**: `references/finance.md` for financial schemas, `references/mnda.md` for company NDA template, `references/policies.md` for company policies, `references/api_docs.md` for API specifications\n- **Use cases**: Database schemas, API documentation, domain knowledge, company policies, detailed workflow guides\n- **Benefits**: Keeps SKILL.md lean, loaded only when the agent determines it's needed\n- **Best practice**: If files are large (>10k words), include grep search patterns in SKILL.md\n- **Avoid duplication**: Information should live in either SKILL.md or references files, not both. Prefer references files for detailed information unless it's truly core to the skill—this keeps SKILL.md lean while making information discoverable without hogging the context window. Keep only essential procedural instructions and workflow guidance in SKILL.md; move detailed reference material, schemas, and examples to references files.\n\n##### Assets (`assets/`)\n\nFiles not intended to be loaded into context, but rather used within the output the agent produces.\n\n- **When to include**: When the skill needs files that will be used in the final output\n- **Examples**: `assets/logo.png` for brand assets, `assets/slides.pptx` for PowerPoint templates, `assets/frontend-template/` for HTML/React boilerplate, `assets/font.ttf` for typography\n- **Use cases**: Templates, images, icons, boilerplate code, fonts, sample documents that get copied or modified\n- **Benefits**: Separates output resources from documentation, enables the agent to use files without loading them into context\n\n#### What to Not Include in a Skill\n\nA skill should only contain essential files that directly support its functionality. Do NOT create extraneous documentation or auxiliary files, including:\n\n- README.md\n- INSTALLATION_GUIDE.md\n- QUICK_REFERENCE.md\n- CHANGELOG.md\n- etc.\n\nThe skill should only contain the information needed for an AI agent to do the job at hand. It should not contain auxiliary context about the process that went into creating it, setup and testing procedures, user-facing documentation, etc. Creating additional documentation files just adds clutter and confusion.\n\n### Progressive Disclosure Design Principle\n\nSkills use a three-level loading system to manage context efficiently:\n\n1. **Metadata (name + description)** - Always in context (~100 words)\n2. **SKILL.md body** - When skill triggers (<5k words)\n3. **Bundled resources** - As needed by the agent (Unlimited because scripts can be executed without reading into context window)\n\n#### Progressive Disclosure Patterns\n\nKeep SKILL.md body to the essentials and under 500 lines to minimize context bloat. Split content into separate files when approaching this limit. When splitting out content into other files, it is very important to reference them from SKILL.md and describe clearly when to read them, to ensure the reader of the skill knows they exist and when to use them.\n\n**Key principle:** When a skill supports multiple variations, frameworks, or options, keep only the core workflow and selection guidance in SKILL.md. Move variant-specific details (patterns, examples, configuration) into separate reference files.\n\n**Pattern 1: High-level guide with references**\n\n```markdown\n# PDF Processing\n\n## Quick start\n\nExtract text with pdfplumber:\n[code example]\n\n## Advanced features\n\n- **Form filling**: See [FORMS.md](FORMS.md) for complete guide\n- **API reference**: See [REFERENCE.md](REFERENCE.md) for all methods\n- **Examples**: See [EXAMPLES.md](EXAMPLES.md) for common patterns\n```\n\nthe agent loads FORMS.md, REFERENCE.md, or EXAMPLES.md only when needed.\n\n**Pattern 2: Domain-specific organization**\n\nFor Skills with multiple domains, organize content by domain to avoid loading irrelevant context:\n\n```\nbigquery-skill/\n├── SKILL.md (overview and navigation)\n└── reference/\n    ├── finance.md (revenue, billing metrics)\n    ├── sales.md (opportunities, pipeline)\n    ├── product.md (API usage, features)\n    └── marketing.md (campaigns, attribution)\n```\n\nWhen a user asks about sales metrics, the agent only reads sales.md.\n\nSimilarly, for skills supporting multiple frameworks or variants, organize by variant:\n\n```\ncloud-deploy/\n├── SKILL.md (workflow + provider selection)\n└── references/\n    ├── aws.md (AWS deployment patterns)\n    ├── gcp.md (GCP deployment patterns)\n    └── azure.md (Azure deployment patterns)\n```\n\nWhen the user chooses AWS, the agent only reads aws.md.\n\n**Pattern 3: Conditional details**\n\nShow basic content, link to advanced content:\n\n```markdown\n# DOCX Processing\n\n## Creating documents\n\nUse docx-js for new documents. See [DOCX-JS.md](DOCX-JS.md).\n\n## Editing documents\n\nFor simple edits, modify the XML directly.\n\n**For tracked changes**: See [REDLINING.md](REDLINING.md)\n**For OOXML details**: See [OOXML.md](OOXML.md)\n```\n\nthe agent reads REDLINING.md or OOXML.md only when the user needs those features.\n\n**Important guidelines:**\n\n- **Avoid deeply nested references** - Keep references one level deep from SKILL.md. All reference files should link directly from SKILL.md.\n- **Structure longer reference files** - For files longer than 100 lines, include a table of contents at the top so the agent can see the full scope when previewing.\n\n## Skill Creation Process\n\nSkill creation involves these steps:\n\n1. Understand the skill with concrete examples\n2. Plan reusable skill contents (scripts, references, assets)\n3. Initialize the skill (run init_skill.py)\n4. Edit the skill (implement resources and write SKILL.md)\n5. Package the skill (run package_skill.py)\n6. Iterate based on real usage\n\nFollow these steps in order, skipping only if there is a clear reason why they are not applicable.\n\n### Skill Naming\n\n- Use lowercase letters, digits, and hyphens only; normalize user-provided titles to hyphen-case (e.g., \"Plan Mode\" -> `plan-mode`).\n- When generating names, generate a name under 64 characters (letters, digits, hyphens).\n- Prefer short, verb-led phrases that describe the action.\n- Namespace by tool when it improves clarity or triggering (e.g., `gh-address-comments`, `linear-address-issue`).\n- Name the skill folder exactly after the skill name.\n\n### Step 1: Understanding the Skill with Concrete Examples\n\nSkip this step only when the skill's usage patterns are already clearly understood. It remains valuable even when working with an existing skill.\n\nTo create an effective skill, clearly understand concrete examples of how the skill will be used. This understanding can come from either direct user examples or generated examples that are validated with user feedback.\n\nFor example, when building an image-editor skill, relevant questions include:\n\n- \"What functionality should the image-editor skill support? Editing, rotating, anything else?\"\n- \"Can you give some examples of how this skill would be used?\"\n- \"I can imagine users asking for things like 'Remove the red-eye from this image' or 'Rotate this image'. Are there other ways you imagine this skill being used?\"\n- \"What would a user say that should trigger this skill?\"\n\nTo avoid overwhelming users, avoid asking too many questions in a single message. Start with the most important questions and follow up as needed for better effectiveness.\n\nConclude this step when there is a clear sense of the functionality the skill should support.\n\n### Step 2: Planning the Reusable Skill Contents\n\nTo turn concrete examples into an effective skill, analyze each example by:\n\n1. Considering how to execute on the example from scratch\n2. Identifying what scripts, references, and assets would be helpful when executing these workflows repeatedly\n\nExample: When building a `pdf-editor` skill to handle queries like \"Help me rotate this PDF,\" the analysis shows:\n\n1. Rotating a PDF requires re-writing the same code each time\n2. A `scripts/rotate_pdf.py` script would be helpful to store in the skill\n\nExample: When designing a `frontend-webapp-builder` skill for queries like \"Build me a todo app\" or \"Build me a dashboard to track my steps,\" the analysis shows:\n\n1. Writing a frontend webapp requires the same boilerplate HTML/React each time\n2. An `assets/hello-world/` template containing the boilerplate HTML/React project files would be helpful to store in the skill\n\nExample: When building a `big-query` skill to handle queries like \"How many users have logged in today?\" the analysis shows:\n\n1. Querying BigQuery requires re-discovering the table schemas and relationships each time\n2. A `references/schema.md` file documenting the table schemas would be helpful to store in the skill\n\nTo establish the skill's contents, analyze each concrete example to create a list of the reusable resources to include: scripts, references, and assets.\n\n### Step 3: Initializing the Skill\n\nAt this point, it is time to actually create the skill.\n\nSkip this step only if the skill being developed already exists, and iteration or packaging is needed. In this case, continue to the next step.\n\nWhen creating a new skill from scratch, always run the `init_skill.py` script. The script conveniently generates a new template skill directory that automatically includes everything a skill requires, making the skill creation process much more efficient and reliable.\n\nUsage:\n\n```bash\nscripts/init_skill.py <skill-name> --path <output-directory> [--resources scripts,references,assets] [--examples]\n```\n\nExamples:\n\n```bash\nscripts/init_skill.py my-skill --path skills/public\nscripts/init_skill.py my-skill --path skills/public --resources scripts,references\nscripts/init_skill.py my-skill --path skills/public --resources scripts --examples\n```\n\nThe script:\n\n- Creates the skill directory at the specified path\n- Generates a SKILL.md template with proper frontmatter and TODO placeholders\n- Optionally creates resource directories based on `--resources`\n- Optionally adds example files when `--examples` is set\n\nAfter initialization, customize the SKILL.md and add resources as needed. If you used `--examples`, replace or delete placeholder files.\n\n### Step 4: Edit the Skill\n\nWhen editing the (newly-generated or existing) skill, remember that the skill is being created for another instance of the agent to use. Include information that would be beneficial and non-obvious to the agent. Consider what procedural knowledge, domain-specific details, or reusable assets would help another the agent instance execute these tasks more effectively.\n\n#### Learn Proven Design Patterns\n\nConsult these helpful guides based on your skill's needs:\n\n- **Multi-step processes**: See references/workflows.md for sequential workflows and conditional logic\n- **Specific output formats or quality standards**: See references/output-patterns.md for template and example patterns\n\nThese files contain established best practices for effective skill design.\n\n#### Start with Reusable Skill Contents\n\nTo begin implementation, start with the reusable resources identified above: `scripts/`, `references/`, and `assets/` files. Note that this step may require user input. For example, when implementing a `brand-guidelines` skill, the user may need to provide brand assets or templates to store in `assets/`, or documentation to store in `references/`.\n\nAdded scripts must be tested by actually running them to ensure there are no bugs and that the output matches what is expected. If there are many similar scripts, only a representative sample needs to be tested to ensure confidence that they all work while balancing time to completion.\n\nIf you used `--examples`, delete any placeholder files that are not needed for the skill. Only create resource directories that are actually required.\n\n#### Update SKILL.md\n\n**Writing Guidelines:** Always use imperative/infinitive form.\n\n##### Frontmatter\n\nWrite the YAML frontmatter with `name` and `description`:\n\n- `name`: The skill name\n- `description`: This is the primary triggering mechanism for your skill, and helps the agent understand when to use the skill.\n  - Include both what the Skill does and specific triggers/contexts for when to use it.\n  - Include all \"when to use\" information here - Not in the body. The body is only loaded after triggering, so \"When to Use This Skill\" sections in the body are not helpful to the agent.\n  - Example description for a `docx` skill: \"Comprehensive document creation, editing, and analysis with support for tracked changes, comments, formatting preservation, and text extraction. Use when the agent needs to work with professional documents (.docx files) for: (1) Creating new documents, (2) Modifying or editing content, (3) Working with tracked changes, (4) Adding comments, or any other document tasks\"\n\nDo not include any other fields in YAML frontmatter.\n\n##### Body\n\nWrite instructions for using the skill and its bundled resources.\n\n### Step 5: Packaging a Skill\n\nOnce development of the skill is complete, it must be packaged into a distributable .skill file that gets shared with the user. The packaging process automatically validates the skill first to ensure it meets all requirements:\n\n```bash\nscripts/package_skill.py <path/to/skill-folder>\n```\n\nOptional output directory specification:\n\n```bash\nscripts/package_skill.py <path/to/skill-folder> ./dist\n```\n\nThe packaging script will:\n\n1. **Validate** the skill automatically, checking:\n\n   - YAML frontmatter format and required fields\n   - Skill naming conventions and directory structure\n   - Description completeness and quality\n   - File organization and resource references\n\n2. **Package** the skill if validation passes, creating a .skill file named after the skill (e.g., `my-skill.skill`) that includes all files and maintains the proper directory structure for distribution. The .skill file is a zip file with a .skill extension.\n\nIf validation fails, the script will report the errors and exit without creating a package. Fix any validation errors and run the packaging command again.\n\n### Step 6: Iterate\n\nAfter testing the skill, users may request improvements. Often this happens right after using the skill, with fresh context of how the skill performed.\n\n**Iteration workflow:**\n\n1. Use the skill on real tasks\n2. Notice struggles or inefficiencies\n3. Identify how SKILL.md or bundled resources should be updated\n4. Implement changes and test again\n","bot/workspace/skills/weather/SKILL.md":"---\nname: weather\ndescription: Get current weather and forecasts (no API key required).\nhomepage: https://wttr.in/:help\nmetadata: {\"vikingbot\":{\"emoji\":\"🌤️\",\"requires\":{\"bins\":[\"curl\"]}}}\n---\n\n# Weather\n\nTwo free services, no API keys needed.\n\n## wttr.in (primary)\n\nQuick one-liner:\n```bash\ncurl -s \"wttr.in/London?format=3\"\n# Output: London: ⛅️ +8°C\n```\n\nCompact format:\n```bash\ncurl -s \"wttr.in/London?format=%l:+%c+%t+%h+%w\"\n# Output: London: ⛅️ +8°C 71% ↙5km/h\n```\n\nFull forecast:\n```bash\ncurl -s \"wttr.in/London?T\"\n```\n\nFormat codes: `%c` condition · `%t` temp · `%h` humidity · `%w` wind · `%l` location · `%m` moon\n\nTips:\n- URL-encode spaces: `wttr.in/New+York`\n- Airport codes: `wttr.in/JFK`\n- Units: `?m` (metric) `?u` (USCS)\n- Today only: `?1` · Current only: `?0`\n- PNG: `curl -s \"wttr.in/Berlin.png\" -o /tmp/weather.png`\n\n## Open-Meteo (fallback, JSON)\n\nFree, no key, good for programmatic use:\n```bash\ncurl -s \"https://api.open-meteo.com/v1/forecast?latitude=51.5&longitude=-0.12&current_weather=true\"\n```\n\nFind coordinates for a city, then query. Returns JSON with temp, windspeed, weathercode.\n\nDocs: https://open-meteo.com/en/docs\n","examples/openclaw-plugin/skills/install-openviking-memory/SKILL.md":"---\nname: install-openviking-memory\ndescription: >\n  Install and configure the OpenViking long-term memory plugin for OpenClaw via natural conversation.\n  Once installed, the plugin automatically captures facts from chats and recalls relevant context\n  before each reply (auto-capture + auto-recall, cross-session). Covers prerequisites, install through\n  OpenClaw's plugin manager (ov-install as backup), wizard-based configuration, slot activation,\n  gateway restart, verification, multi-tenant root keys, multi-instance and uninstall. The user needs\n  no CLI knowledge — the agent runs everything and only asks for a few values. Assumes the OpenViking\n  server is already running; it does NOT install the server. Trigger on: \"install OpenViking\",\n  \"set up memory\", \"configure memory plugin\", \"add long-term memory\", \"semantic memory\", \"RAG\",\n  \"帮我装 OpenViking\", \"配置记忆插件\", \"安装记忆功能\", \"接入 OpenViking\".\nversion: 2026.6.5\nmetadata:\n  openclaw:\n    requires:\n      bins:\n        - node\n        - openclaw\n  emoji: \"🦣\"\n  homepage: \"https://github.com/volcengine/OpenViking\"\ntags:\n  - install\n  - setup\n  - memory\n  - openviking\n  - rag\n  - long-term-memory\n---\n\n# Install OpenViking Memory — Agent Installation & Reference Skill\n\nYou are about to help a user install and configure the OpenViking memory plugin for OpenClaw.\nThe 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.\n\n**Read this entire document before starting. Follow every step in order. Do NOT skip steps.**\n\nThis skill is split into two halves:\n\n- **Part 1 (STEP 0–10): Natural-language installation walkthrough.** Use this on first install or reconfigure.\n- **Part 2: Reference.** Tools, config schema, multi-tenant, multi-instance, daily ops, uninstall, error recovery. Read on demand.\n\n> **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.\n\n---\n\n# Part 1 — Natural-Language Installation Walkthrough\n\n## STEP 0: Detect Language\n\nIf the user's first message contains Chinese characters, respond in **Chinese** throughout.\nOtherwise respond in **English**.\nAll user-facing messages below have (CN)/(EN) variants — use the matching one.\n\nDo NOT show this step to the user.\n\n---\n\n## STEP 1: Detect Operating System (silently)\n\nRun this command and remember the result. Do NOT show it to the user.\n\n```bash\nuname -s 2>/dev/null || echo WINDOWS\n```\n\n- Output contains `Darwin` → `OS=mac`\n- Output contains `Linux` → `OS=linux`\n- Output is `WINDOWS` or the command fails → `OS=windows`\n\n---\n\n## STEP 2: Check Prerequisites (silently)\n\nRun silently:\n\n```bash\nnode -v\nopenclaw --version\n```\n\n**If `node` is missing:**\n\n> (CN) 你的系统没有安装 Node.js。OpenClaw 和 OpenViking 插件需要 Node.js >= 22。请先安装 Node.js，然后再回来找我。\n> (EN) Node.js is not installed. OpenClaw and the OpenViking plugin require Node.js >= 22. Please install Node.js first, then come back.\n\n**Stop. Do NOT continue.**\n\n**If `openclaw` is missing:**\n\n> (CN) 你的系统没有安装 OpenClaw。请先安装 OpenClaw（>= 2026.5.27），然后再回来找我。\n> (EN) OpenClaw is not installed. Please install OpenClaw (>= 2026.5.27) first, then come back.\n\n**Stop. Do NOT continue.**\n\nIf both exist, proceed to STEP 3 silently.\n\n---\n\n## STEP 3: Greet and Ask for 3 Values\n\nSend this message:\n\n> (CN) 好，我来帮你接入 OpenViking 长期记忆。装好之后，我会自动记住对话里的重要信息，下次聊也能回忆起来。\n>\n> 我需要 3 条信息，不知道的可以问你的管理员：\n> 1. **OpenViking 服务地址** —— 例如 `https://ov.example.com` 或 `http://192.168.1.100:1933`，本机服务可以直接说\"本机\"\n> 2. **API Key** —— 用来鉴权；服务没开认证可以说\"没有\"\n> 3. **记忆归属**（可选） —— 所有对话共用 user 记忆（默认），还是在 `peers/<assistant_id>` 或 `peers/<sender_id>` 下分开存放\n>\n> 先告诉我服务地址吧？\n\n> (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.\n>\n> I need 3 things (ask your admin if unsure):\n> 1. **OpenViking server URL** — e.g. `https://ov.example.com` or `http://192.168.1.100:1933`. For a local server, just say \"local\".\n> 2. **API Key** — for auth. Say \"none\" if the server has no auth.\n> 3. **Memory scope** (optional) — shared user memory (default), or peer memory under `peers/<assistant_id>` / `peers/<sender_id>`.\n>\n> What's the server URL?\n\n---\n\n## STEP 4: Collect Values\n\nCollect 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.\n\n### 4a. `BASE_URL` (REQUIRED)\n\n- \"local\" / \"本机\" / \"localhost\" → use `http://127.0.0.1:1933`.\n- `ov.example.com` without protocol → prepend `https://`.\n- Strip trailing `/`, `/health`, or `/api`.\n- After normalization must start with `http://` or `https://`.\n- 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.**\n\n### 4b. `API_KEY` (OPTIONAL)\n\n> (CN) API Key 是什么？服务没开认证就直接说\"没有\"。\n> (EN) What's the API Key? Say \"none\" if the server has no auth.\n\n- \"no\" / \"none\" / \"没有\" / \"空\" / empty → `API_KEY=\"\"` (you will skip the flag later).\n- Otherwise store as-is.\n\n### 4c. `PEER_ROLE` (OPTIONAL)\n\n> (CN) 记忆要存在哪一层？默认共享在 `viking://user/<user_id>/memories`（`none`）。也可以按 OpenClaw 助手存到 `.../peers/<assistant_id>/memories`（`assistant`），或按发送者存到 `.../peers/<sender_id>/memories`（`sender`）。\n> (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`).\n\nDefault to `none`. **Never silently pick another value** — the three options mean different storage layouts and are awkward to change later:\n\n| Value | Concrete layout | Choose it when |\n|---|---|---|\n| `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. |\n| `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/...`. |\n| `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. |\n\n`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`.\n\nOpenViking 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.\n\n- Empty / \"default\" / \"默认\" / \"共用\" → leave unset (plugin defaults to `none`).\n- Otherwise pass `--peer-role none|assistant|sender`. Treat explicit legacy `person` as `sender`. Any other value → ask again.\n\n### 4d. `PEER_PREFIX` (OPTIONAL, only when `PEER_ROLE=assistant`)\n\n> (CN) 想给这个 agent 一个记忆前缀吗？留空就用默认。只能用字母、数字、`_`、`-`。\n> (EN) Want to set an peer prefix? Leave blank for the default. Letters, digits, `_`, `-` only.\n\n- Only meaningful when `PEER_ROLE=assistant`; skip the question otherwise.\n- Empty / \"default\" / \"默认\" → leave unset (plugin defaults to `\"\"`).\n- Otherwise validate against `/^[A-Za-z0-9_-]+$/`. If invalid, ask again.\n\n### 4e. (Conditional) Multi-Tenant Root-Key Fields\n\nOnly ask for these if STEP 7 detects a root key (`Root API key detected. Missing: --account-id, --user-id`). Don't ask up front.\n\n- `ACCOUNT_ID`\n- `USER_ID`\n\nSee **Reference: Multi-Tenant** for what these mean.\n\n---\n\n## STEP 5: Pre-flight Connectivity Check (silently)\n\nTell the user briefly:\n\n> (CN) 我先测一下能不能连上服务……\n> (EN) Let me test the connection to your server...\n\nRun:\n\n**If OS=windows:**\n\n```powershell\ntry { (Invoke-WebRequest -Uri \"BASE_URL/health\" -TimeoutSec 10 -UseBasicParsing -ErrorAction Stop).StatusCode } catch { $_.Exception.Response.StatusCode.value__ }\n```\n\n**If OS=mac or OS=linux:**\n\n```bash\ncurl -sS -o /dev/null -w \"%{http_code}\" --connect-timeout 10 \"BASE_URL/health\"\n```\n\nReplace `BASE_URL` with the actual value.\n\n| Status | Meaning | Action |\n|---|---|---|\n| `200` | Server reachable, no auth on `/health` | Proceed to STEP 6. |\n| `401` / `403` | Server reachable but `/health` requires auth | Proceed to STEP 6 — the wizard's key probe will sort it out. |\n| `000` / timeout / connection refused | Server unreachable | **Server-not-ready handling** below. |\n| Anything else | Unexpected | Show status code to the user, go back to STEP 4a. |\n\n### Server-not-ready handling\n\nThis skill **does not install or operate the OpenViking server**. If the user's server is unreachable, present the situation honestly and offer two paths:\n\n> (CN) ❌ 我连不上 `BASE_URL`。可能是：\n> 1) 服务还没启动 —— 请联系你的 OpenViking 服务管理员把它起起来；如果是你自己负责，请参考 OpenViking 官方文档（`https://github.com/volcengine/OpenViking`）的 server 启动指引。\n> 2) 地址不对 —— 你可以重新告诉我正确的地址。\n> 3) 网络不通（防火墙 / VPN / 内网）—— 你确认一下网络。\n>\n> 也可以选择\"先把配置写下来\"，等服务起来就自动生效，要这么办吗？\n\n> (EN) ❌ Cannot reach `BASE_URL`. Likely cause:\n> 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.**\n> 2) **Wrong URL** — give me the correct URL.\n> 3) **Network blocked** (firewall / VPN / private network) — please verify connectivity.\n>\n> Or I can save the config now (`--allow-offline`) so it will activate automatically once the server is up. Want me to do that?\n\nIf the user fixes the URL → back to STEP 4a.\nIf the user wants `--allow-offline` → remember `ALLOW_OFFLINE=true` and continue to STEP 6.\nIf the user gives up / cannot fix → stop here. Do NOT continue with a broken state.\n\n---\n\n## STEP 6: Install the Plugin\n\nThe 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.\n\n### Path A — Primary: `openclaw plugins install` (uses ClawHub)\n\nTell the user:\n\n> (CN) 现在开始装插件……\n> (EN) Installing the plugin now...\n\nRun:\n\n```bash\nopenclaw plugins install clawhub:@openviking/openclaw-plugin\n```\n\nTrigger fallback to Path B only if the output contains any of these strings:\n\n- `429`\n- `rate limit` / `rate-limited` / `Too Many Requests`\n- `not logged in` / `please log in` / `please login` / `unauthorized` / `401` / `403` together with `clawhub`\n- `ETIMEDOUT` / `ECONNRESET` on a `clawhub`-related host\n- generic message indicating the registry refused an anonymous client\n\nBefore falling back, also try the explicit registry prefix once:\n\n```bash\nopenclaw plugins install clawhub:@openviking/openclaw-plugin\n```\n\nIf the install **succeeds**, jump to STEP 7.\n\nIf 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.\n\n### Path B — Backup: `ov-install` (bypasses ClawHub)\n\nTell the user:\n\n> (CN) ClawHub 现在好像被限流、不可用，或者当前账号不能安装。我改用备用路径，通过 npm 下载并部署插件包。\n> (EN) ClawHub looks rate-limited, unavailable, or blocked for this account. I'll use the backup path and install the plugin package from npm.\n\nRun the installer with `npx` (no global install needed):\n\n```bash\nnpx -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]\n```\n\nBuild the flag list according to what the user gave you:\n\n- Always pass `--base-url BASE_URL`.\n- Pass `--api-key API_KEY` only if `API_KEY` is non-empty.\n- 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.\n- Pass `--peer-prefix PEER_PREFIX` only if the user gave one and `PEER_ROLE=assistant`.\n- `--account-id` / `--user-id` only if the root-key path requires them.\n\n`ov-install` will, in one shot:\n1. Download the `@openviking/openclaw-plugin` package from npm into a temporary staging dir.\n2. Copy the package into the OpenClaw `extensions/` dir and install plugin dependencies.\n3. Register the plugin in `openclaw.json` (via `openclaw plugins enable` or direct write).\n4. Run `openclaw openviking setup --json --base-url … [--api-key …]` for the user.\n5. Return a non-zero exit if setup needs explicit `--allow-offline` or `--force-slot` consent.\n\nThis 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).\n\nIf `ov-install` exits non-zero, capture the last 30 lines of its output, show them to the user, and stop. Don't retry blindly.\n\n---\n\n## STEP 7: Configure (only on Path A — Path B did this for you)\n\nRun the setup wizard non-interactively. Build flags from collected values:\n\n```bash\nopenclaw 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]\n```\n\nRules:\n\n- `--base-url BASE_URL` is **required** under `--json`. Without it, the wizard prints `--json requires --base-url for non-interactive mode`.\n- `--api-key` only if `API_KEY` is non-empty.\n- `--peer-role` only if the user chose `assistant` or `sender`; omit it for the `none` default. Normalize legacy `person` to `sender` first.\n- `--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.\n- `--account-id` / `--user-id` only after STEP 7 root-key detection (see below).\n- `--allow-offline` only if the user explicitly approved it in STEP 5.\n- `--force-slot` **never** in the first attempt. Add only after the user confirms (see slot_blocked handling below).\n\n### Parse the JSON output\n\nThe wizard prints a single JSON object:\n\n```json\n{\n  \"success\": true | false,\n  \"action\": \"configured\" | \"existing\" | \"error\" | \"slot_blocked\",\n  \"config\": { \"mode\": \"remote\", \"baseUrl\": \"...\", \"apiKey\": \"...\", \"peer_prefix\": \"...\", \"accountId\": \"...\", \"userId\": \"...\" },\n  \"health\": { \"ok\": true, \"version\": \"...\", \"compatibility\": \"compatible\" | \"server_too_old\" | \"server_too_new\" | \"unknown\" },\n  \"keyProbe\": { \"keyType\": \"user_key\" | \"root_key\" | \"no_key\" | \"unknown\", \"needsAccountId\": false, \"needsUserId\": false, \"detail\": \"...\" },\n  \"slot\": { \"activated\": true, \"replaced\": false, \"previousOwner\": \"openviking\" },\n  \"error\": \"...\"\n}\n```\n\n### Decision matrix\n\n| Condition | Action |\n|---|---|\n| `success: true` and `action: \"configured\"` or `\"existing\"` | Done. Proceed to STEP 9. |\n| `success: false` and `action: \"slot_blocked\"` | **Slot conflict — see below.** |\n| `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. |\n| `success: false` and `error` contains `\"Root API key detected\"` and `\"Missing: --account-id, --user-id\"` | **Root-key path — see below.** |\n| `success: false` and `error` contains `\"Invalid API key\"` / `keyProbe.keyType: \"unknown\"` after auth failure detail | API key wrong. Back to STEP 4b. |\n| `success: false` and any other `action: \"error\"` | Show `error` to the user, stop. Do NOT pretend success. |\n\n### Slot conflict (`slot_blocked`)\n\nThe error text looks like:\n\n> `contextEngine slot is owned by \"<other-plugin>\". Config was saved but slot was NOT changed. Use --force-slot to replace.`\n\n**Do NOT silently use `--force-slot`.** Ask the user:\n\n> (CN) 你的 OpenClaw 当前 contextEngine 槽被 `<other-plugin>` 占着。如果用 OpenViking 替换它，`<other-plugin>` 就不再生效。要替换吗？\n> (EN) Your `contextEngine` slot is currently owned by `<other-plugin>`. Activating OpenViking will disable it. Replace?\n\nIf 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.\n\n### Root-key path\n\nThe error text looks like:\n\n> `Root API key detected. Missing: --account-id, --user-id. Provide both to enable multi-tenant scoping.`\n\nAsk the user:\n\n> (CN) 你给的是 root 级 API Key，需要再补两个值才能用：账户 ID（accountId）和用户 ID（userId）。这两个一般是 OpenViking 服务管理员配的，不知道就问他们。\n> (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.\n\nAfter collecting, retry the setup command with `--account-id` and `--user-id` appended.\n\n---\n\n## STEP 8: (Reserved — done inside STEP 7 or by `ov-install`)\n\nThe 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.\n\n---\n\n## STEP 9: Restart the Gateway\n\n```bash\nopenclaw gateway restart\n```\n\nIf it fails, try once more with:\n\n```bash\nopenclaw gateway --force\n```\n\nIf both fail:\n\n> (CN) Gateway 没能自动重启。请你手动跑一下 `openclaw gateway restart`。重启完告诉我，我来验证。\n> (EN) Gateway didn't restart cleanly. Please run `openclaw gateway restart` manually, then tell me when it's done so I can verify.\n\nWait ~3 seconds before STEP 10.\n\n---\n\n## STEP 10: Verify\n\n```bash\nopenclaw openviking status --json\n```\n\nExpected output:\n\n```json\n{\n  \"configured\": true,\n  \"slotActive\": true,\n  \"health\": { \"ok\": true },\n  \"config\": { \"baseUrl\": \"...\", \"peer_prefix\": \"...\" }\n}\n```\n\n### Decision\n\n- `configured: true` **and** `slotActive: true` **and** `health.ok: true` → **success.** Send the success message below.\n- `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.\n- `configured: true` but `health.ok: false` → server became unreachable. Tell the user; the config will start working as soon as the server is back.\n- `configured: false` → setup didn't actually persist. Show full status JSON to the user and stop.\n\n### Success message\n\n> (CN) 🎉 全部搞定！OpenViking 长期记忆已经接好了。\n>\n> 工作方式：\n> - **每一轮**：你的对话会被自动追加到 OpenViking session 里；自动抽取依赖阈值 commit 或 `/compact`\n> - **明确要记住时**：如果你说“记住/保存/存一下”某个长期事实，我会用 `memory_store` 立即提交到记忆管线\n> - **`/compact` 时**：待提交的 session 消息会在 OpenViking 服务端被抽取成长期记忆\n> - **后续会话**：每次回复前我都会自动搜一下相关记忆并带进上下文\n>\n> 想验证一下吗？可以直接说“记住我的邮箱是 test@example.com”，让我通过 `memory_store` 立即提交；或者先聊几句包含可记忆事实的话，再调一下 `/compact`。之后开新对话问“我的邮箱是？”，应该就能召回了。\n\n> (EN) 🎉 All set! OpenViking long-term memory is connected.\n>\n> How it works:\n> - **Every turn**: our messages are appended to an OpenViking session; automatic extraction depends on a threshold commit or `/compact`\n> - **Explicit remember requests**: if you ask me to remember/save/store a long-term fact, I can use `memory_store` to commit it immediately\n> - **On `/compact`**: pending session messages are extracted into long-term memories on the OpenViking server\n> - **Future sessions**: relevant memories are auto-retrieved and injected before my replies\n>\n> 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.\n\n---\n\n# Part 2 — Reference\n\n## How It Works\n\nThe 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:\n\n- **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/`.\n- **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.\n  - `captureMode: \"semantic\"` (default): server extraction pipeline filters all qualifying text.\n  - `captureMode: \"keyword\"`: only text matching trigger words (e.g. \"remember\", \"preference\") is considered.\n- **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.\n\n**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.\n\n### Explicit long-term memory writes\n\nAuto-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.\n\nUse `memory_store` as the integration-side reliable path for durable-memory intent:\n\n- It writes the supplied text into an OpenViking session and calls `commit(wait=true)`.\n- It complements auto-capture; it does not replace normal session capture.\n- 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.\n\n## Available Tools\n\nThese are the plugin tools the agent can call once installed.\n\n### `memory_recall` — Search Memories\n\n| Parameter | Required | Description |\n|---|---|---|\n| `query` | Yes | Search query text |\n| `limit` | No | Maximum number of results (defaults to plugin config) |\n| `scoreThreshold` | No | Minimum relevance score 0–1 (defaults to plugin config) |\n| `targetUri` | No | Exact search scope URI. If provided, only that URI is searched. |\n| `resourceTypes` | No | Array of target types used when `targetUri` is omitted: `resource`, `user`, `agent`. Defaults to plugin `recallTargetTypes`. |\n\nExample: user asks \"What programming language did I say I like?\"\n\n### `memory_store` — Manual Store\n\n| Parameter | Required | Description |\n|---|---|---|\n| `text` | Yes | Information text to store |\n| `role` | No | Session role (default `user`) |\n| `sessionId` | No | Existing OpenViking session ID |\n\nUse this when the user explicitly asks to remember/save/store a long-term fact, preference, project, or decision.\n\nExample: user says \"Remember my email is xxx@example.com\".\n\n### `memory_forget` — Delete Memories\n\n| Parameter | Required | Description |\n|---|---|---|\n| `uri` | No | Exact memory URI (direct delete) |\n| `query` | No | Search query (find then delete) |\n| `targetUri` | No | Search scope URI |\n| `limit` | No | Search limit (default 5) |\n| `scoreThreshold` | No | Minimum relevance score |\n\nExample: user says \"Forget my phone number\".\n\n### `ov_archive_search` — Search Archived Original Messages\n\n| Parameter | Required | Description |\n|---|---|---|\n| `query` | Yes | A single keyword or short phrase. Prefer concrete names, dates, commands, paths, or distinctive nouns. |\n| `archiveId` | No | Optional archive ID such as `archive_002`. |\n\nUse when `[Session History Summary]` is too coarse. Try at least two keyword variants before concluding the detail is unavailable.\n\n### `ov_archive_expand` — Expand An Archive\n\n| Parameter | Required | Description |\n|---|---|---|\n| `archiveId` | Yes | Archive ID from `[Archive Index]`, e.g. `archive_002`. |\n\n### `add_resource` — Import Resource\n\nThe 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.\n\n| Parameter | Required | Description |\n|---|---|---|\n| `source` | Yes | Local path, OpenClaw media attachment path, directory path, public URL, or Git URL. |\n| `to` | No | Exact target URI under `viking://resources`; mutually exclusive with `parent`. |\n| `parent` | No | Parent URI under `viking://resources`; mutually exclusive with `to`. |\n| `reason` | No | Reason or note for import. |\n| `instruction` | No | Processing instruction for semantic extraction. |\n| `wait` | No | Wait for processing completion. |\n\n### `add_skill` — Import Agent Skill\n\n| Parameter | Required | Description |\n|---|---|---|\n| `source` | No | Local `SKILL.md` path or skill directory. Exactly one of `source` or `data` is required. |\n| `data` | No | Raw `SKILL.md` content or MCP tool dict. Exactly one of `source` or `data` is required. |\n| `wait` | No | Wait for processing completion. |\n\nAgent 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.\n\n### `ov_search` — Search Resources and Skills\n\n| Parameter | Required | Description |\n|---|---|---|\n| `query` | Yes | Search query. |\n| `uri` | No | Optional search URI. Defaults to resources plus agent skills. |\n| `limit` | No | Max results per search scope. Default: 10. |\n\nImportant: `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.\n\n### `ov_read` — Read OpenViking Content\n\n| Parameter | Required | Description |\n|---|---|---|\n| `uri` | Yes | Exact `viking://...` URI returned by `ov_search` or recall trace results. Local file paths and `openviking://...` display aliases are refused. |\n\n### `ov_recall_trace` — Query Recall Trace\n\n| Parameter | Required | Description |\n|---|---|---|\n| `turn` | No | `latest` or `all`; default `latest`. |\n| `traceId` | No | Exact trace ID. |\n| `sessionId` / `sessionKey` / `ovSessionId` | No | Session filters. |\n| `source` | No | `auto_recall`, `memory_recall`, `ov_search`, or `ov_archive_search`. |\n| `resourceTypes` | No | Target type filters: `resource`, `user`, `agent`. |\n| `since` / `until` | No | Unix timestamp bounds in milliseconds. |\n| `includeContent` | No | Read selected/displayed URI content previews on demand. |\n| `limit` | No | Maximum traces to return. Default: 20. |\n\nRequires `traceRecall=true`; persisted lookup also requires `traceRecallPersist=true`.\n\n### Externalized Tool Result Tools\n\nUse these when a preview contains a `viking://session/<session_id>/tool-results/<tool_result_id>` ref.\n\n| Tool | Parameters |\n|---|---|\n| `openviking_tool_result_list` | `tool_name?`, `limit?` (default 50) |\n| `openviking_tool_result_search` | `tool_output_ref`, `query`, `limit?` (default 20), `context_chars?` (default 300) |\n| `openviking_tool_result_read` | `tool_output_ref`, `offset?` (default 0), `limit?` (default 20000) |\n\n## Configuration Schema\n\nThese are the keys under `plugins.entries.openviking.config` in `openclaw.json`. The setup wizard / `ov-install` sets the first few; the rest are tunables.\n\n| Field | Default | Description |\n|---|---|---|\n| `mode` | `\"remote\"` (forced by plugin) | Always remote in this skill. Don't set manually. |\n| `baseUrl` | `http://127.0.0.1:1933` | OpenViking server URL. |\n| `apiKey` | — | API key. Optional if server has no auth. |\n| `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`. |\n| `peer_prefix` | `\"\"` | Optional prefix for assistant `peer_id` / actor peer values. Only used when `peer_role=assistant`. Letters / digits / `_` / `-`. |\n| `accountId` | — | Required when `apiKey` is a root key. |\n| `userId` | — | Required when `apiKey` is a root key. |\n| `targetUri` | `viking://~/memories` | Default search scope URI. |\n| `timeoutMs` | (plugin default) | HTTP timeout for OpenViking calls. |\n| `autoCapture` | `true` | Auto-append turn messages to the OpenViking session at `afterTurn`; extraction runs only after a threshold commit, `/compact`, or explicit `memory_store`. |\n| `captureMode` | `\"semantic\"` | Filter mode used by the server-side extraction pipeline: `semantic` or `keyword`. |\n| `captureMaxLength` | `24000` | Max text length per archived turn. |\n| `autoRecall` | `true` | Auto-recall and inject memories before reply. |\n| `recallTargetTypes` | `user,agent` | Default target types when `targetUri` is omitted. Allowed: `resource`, `user`, `agent`. |\n| `recallResources` | `false` | Compatibility shortcut that appends `resource` to default recall targets when `recallTargetTypes` is unset. |\n| `recallLimit` | `6` | Max memories injected per recall. |\n| `recallScoreThreshold` | `0.15` | Min relevance score to inject. |\n| `recallMaxInjectedChars` | `4000` | Hard cap on injected character count. Complete memories that do not fit are skipped, not truncated. |\n| `recallPreferAbstract` | (plugin default) | Prefer abstract memories over raw. |\n| `recallTokenBudget` | deprecated | Compatibility alias for `recallMaxInjectedChars`. |\n| `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. |\n| `commitKeepRecentCount` | `10` | Recent messages kept live after afterTurn commit. Compact always uses `0`. |\n| `bypassSessionPatterns` | — | Glob patterns for sessions skipped by capture. |\n| `ingestReplyAssist` | (plugin default) | Reply-assist ingestion toggle. |\n| `emitStandardDiagnostics` | `false` | Emit structured `openviking: diag {...}` lines. |\n| `logFindRequests` | `false` | Log routing for find/session writes. Also enabled by `OPENVIKING_LOG_ROUTING=1` or `OPENVIKING_DEBUG=1`. |\n| `traceRecall` | `false` | Record recall traces in memory. |\n| `traceRecallPersist` | `false` | Persist recall traces to JSONL files. |\n| `traceRecallDir` | `~/.openclaw/openviking/recall-traces` | Recall trace directory. |\n\nTo change a value:\n\n```bash\nopenclaw config set plugins.entries.openviking.config.<field> <value>\nopenclaw gateway restart\n```\n\n## Multi-Tenant (Root API Keys)\n\nSome 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:\n\n```\nRoot API key detected. Missing: --account-id, --user-id\n```\n\nWhen you see this:\n\n1. Ask the user for both values (they come from the OpenViking admin).\n2. Retry STEP 7 with both flags:\n\n```bash\nopenclaw openviking setup --base-url BASE_URL --api-key API_KEY --account-id ACCOUNT_ID --user-id USER_ID --json\n```\n\nA **user key** (issued per tenant) does not need these flags.\n\n## Multi-Instance (`--workdir` / `OPENCLAW_STATE_DIR`)\n\nIf the user runs multiple OpenClaw instances (e.g. testing several agents in parallel), each has its own state dir.\n\nTo target a non-default instance:\n\n```bash\nnpx -y openclaw-openviking-setup-helper@latest --workdir ~/.openclaw-second --base-url ... --api-key ...\n```\n\n`ov-install` writes a helper env file when the state dir is non-default:\n\n- Unix: `~/.openclaw/openviking.env` containing `export OPENCLAW_STATE_DIR='...'`\n- Windows: `~/.openclaw/openviking.env.bat` and `.ps1` setting the same variable\n\nSource it before running `openclaw` commands so they hit the correct state:\n\n**Unix:**\n```bash\nsource ~/.openclaw/openviking.env\nopenclaw status\n```\n\n**Windows (PowerShell):**\n```powershell\n. \"$HOME/.openclaw/openviking.env.ps1\"\nopenclaw status\n```\n\nOr pass `--workdir` directly to each `openclaw` invocation (note: not all `openclaw` subcommands honor `--workdir` consistently — when in doubt, prefer the env var).\n\n## Daily Operations\n\n```bash\n# Start or restart OpenClaw gateway after config changes\nopenclaw gateway restart\n\n# Check overall status\nopenclaw status\nopenclaw openviking status --json\n\n# Read current OpenViking slot\nopenclaw config get plugins.slots.contextEngine\n\n# Disable OpenViking memory (keep config, deactivate slot)\nopenclaw config set plugins.slots.contextEngine legacy\nopenclaw gateway restart\n\n# Re-enable\nopenclaw config set plugins.slots.contextEngine openviking\nopenclaw gateway restart\n```\n\n## Uninstall\n\n### Preferred: via OpenClaw plugin manager\n\n```bash\nopenclaw plugins uninstall openviking\nopenclaw config set plugins.slots.contextEngine legacy\nopenclaw gateway restart\n```\n\n### If installed through backup `ov-install`\n\n```bash\nnpx -y openclaw-openviking-setup-helper@latest --uninstall\n```\n\nThis will:\n\n1. Stop the OpenClaw gateway.\n2. Back up `openclaw.json`.\n3. Remove all OpenViking plugin config from `openclaw.json` (`plugins.entries.openviking`, `plugins.allow`, `plugins.installs.openviking`, `plugins.slots.contextEngine`).\n4. Move the plugin directory to `disabled-extensions/` as backup.\n5. Remove the helper env files (`openviking.env`, `.bat`, `.ps1`).\n\nAdd `--base-url dummy` for fully non-interactive mode (skips the confirmation prompt).\n\n### Manual\n\n```bash\nopenclaw gateway stop\nopenclaw config set plugins.slots.contextEngine legacy\n# Then edit ~/.openclaw/openclaw.json and remove openviking from\n#   plugins.entries\n#   plugins.allow\n#   plugins.installs\nopenclaw gateway restart\n```\n\n## Error Recovery Reference\n\nMatch against actual stderr / JSON `error` strings.\n\n| Symptom (exact string) | Likely cause | Fix |\n|---|---|---|\n| `command not found: openclaw` | OpenClaw not installed | Stop. Ask user to install OpenClaw >= 2026.5.27. |\n| `command not found: node` / `'node' is not recognized` | Node missing | Stop. Ask user to install Node.js >= 22. |\n| `429` / `rate limit` / `Too Many Requests` from `plugins install` | ClawHub throttle | Switch to backup Path B (`ov-install`). |\n| `not logged in` / `please log in` from `plugins install` | Anonymous user hit limit | Switch to backup Path B (`ov-install`). |\n| `ECONNREFUSED` / `connection refused` on `BASE_URL/health` | Server not running | Server-not-ready handling in STEP 5. |\n| `ETIMEDOUT` on `BASE_URL/health` | Network blocked | Ask about firewall / VPN / proxy. |\n| `--json requires --base-url for non-interactive mode` | Built command wrong | Add `--base-url BASE_URL` and retry. |\n| `Server unreachable: …. Use --allow-offline to save config anyway.` | Setup couldn't reach server | Offer `--allow-offline`. |\n| `contextEngine slot is owned by \"<x>\". … Use --force-slot to replace.` | Slot conflict | Ask user, then retry with `--force-slot`. |\n| `Root API key detected. Missing: --account-id, --user-id` | Multi-tenant key | Collect both, retry with `--account-id` `--user-id`. |\n| `openviking: config parse failed` (in gateway log) | Bad value in `openclaw.json` | Show user; check `peer_prefix` charset, URL format. |\n| `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. |\n| `401` / `403` on plugin requests, but `/health` works | Server requires auth on API endpoints | Re-run STEP 7 with the correct `--api-key`. |\n| 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. |\n\n## Important Rules\n\n1. **Never ask the user to run commands.** You run everything via your shell tool.\n2. **Never skip STEP 5 (connectivity check).** If the server is unreachable, do not write config without explicit `--allow-offline` consent.\n3. **Never silently use `--force-slot`.** Slot replacement disables another plugin — always confirm with the user first.\n4. **Never invent values.** If the user can't provide a required value, stop and tell them what to ask their admin.\n5. **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.\n6. **Use `--peer-prefix` for assistant peer prefixes.** The old ID-style setup flag is no longer supported by the schema.\n7. **For Windows, use PowerShell equivalents.** Don't rely on `nohup`, `&`, `mkdir -p`, `source`, etc.\n8. **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.\n9. **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.\n10. **Be brief and friendly in user-visible text.** Save technical detail for when something actually fails.\n11. **Do NOT use `clawhub install openviking`.** That installs a different thing (an AgentSkill, not the plugin).\n","examples/opencode/plugin/skills/openviking/SKILL.md":"# openviking\n\nPath: `examples/opencode/plugin/skills/openviking/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/volcengine/OpenViking/HEAD/examples/opencode/plugin/skills/openviking/SKILL.md)","examples/openclaw-plugin/openclaw.plugin.json":"# openclaw-plugin Plugin Manifest\n\nPath: `examples/openclaw-plugin/openclaw.plugin.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/volcengine/OpenViking/HEAD/examples/openclaw-plugin/openclaw.plugin.json)","bot/workspace/skills/README.md":"# Skills Skill\n\nPath: `bot/workspace/skills/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/volcengine/OpenViking/HEAD/bot/workspace/skills/README.md)","examples/codex-memory-plugin/.codex-plugin/plugin.json":"# codex-plugin Plugin Manifest\n\nPath: `examples/codex-memory-plugin/.codex-plugin/plugin.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/volcengine/OpenViking/HEAD/examples/codex-memory-plugin/.codex-plugin/plugin.json)","examples/claude-code-memory-plugin/.claude-plugin/plugin.json":"# claude-plugin Plugin Manifest\n\nPath: `examples/claude-code-memory-plugin/.claude-plugin/plugin.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/volcengine/OpenViking/HEAD/examples/claude-code-memory-plugin/.claude-plugin/plugin.json)","bot/workspace/skills/opencode/status.json":"# Opencode Skill\n\nPath: `bot/workspace/skills/opencode/status.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/volcengine/OpenViking/HEAD/bot/workspace/skills/opencode/status.json)"},"items":[{"name":"SKILL.md","path":"bot/workspace/skills/cron/SKILL.md","rawUrl":"https://raw.githubusercontent.com/volcengine/OpenViking/HEAD/bot/workspace/skills/cron/SKILL.md","title":"cron","category":"anthropic-skill","format":"markdown","content":"---\nname: cron\ndescription: Schedule reminders and recurring tasks.\n---\n\n# Cron\n\nUse the `cron` tool to schedule reminders or recurring tasks.\n\n## Three Modes\n\n1. **Reminder** - message is sent directly to user\n2. **Task** - message is a task description, agent executes and sends result\n3. **One-time** - runs once at a specific time, then auto-deletes\n\n## Examples\n\nFixed reminder:\n```\ncron(action=\"add\", message=\"Time to take a break!\", every_seconds=1200)\n```\n\nDynamic task (agent executes each time):\n```\ncron(action=\"add\", message=\"Check HKUDS/vikingbot GitHub stars and report\", every_seconds=600)\n```\n\nOne-time scheduled task (compute ISO datetime from current time):\n```\ncron(action=\"add\", message=\"Remind me about the meeting\", at=\"<ISO datetime>\")\n```\n\nList/remove:\n```\ncron(action=\"list\")\ncron(action=\"remove\", job_id=\"abc123\")\n```\n\n## Time Expressions\n\n| User says | Parameters |\n|-----------|------------|\n| every 20 minutes | every_seconds: 1200 |\n| every hour | every_seconds: 3600 |\n| every day at 8am | cron_expr: \"0 8 * * *\" |\n| weekdays at 5pm | cron_expr: \"0 17 * * 1-5\" |\n| at a specific time | at: ISO datetime string (compute from current time) |\n","frontmatter":{"name":"cron","description":"Schedule reminders and recurring tasks."},"isInternal":false,"tokens":306,"sizeBytes":1171},{"name":"SKILL.md","path":"bot/workspace/skills/github-proxy/SKILL.md","rawUrl":"https://raw.githubusercontent.com/volcengine/OpenViking/HEAD/bot/workspace/skills/github-proxy/SKILL.md","title":"github-proxy","category":"anthropic-skill","format":"markdown","content":"---\nname: github-proxy\ndescription: GitHub 国内访问加速 skill，使用 githubproxy.cc 代理加速 GitHub 仓库克隆、文件下载、Raw 文件访问等操作。使用场景：(1) 需要 git clone GitHub 仓库时加速，(2) 下载 GitHub Release 文件、Raw 文件、Archive 压缩包时加速，(3) 任何需要访问 GitHub 资源但速度慢的场景\n---\n\n# GitHub 国内代理加速 Skill\n\n使用 githubproxy.cc 代理服务，为国内访问 GitHub 提供加速支持。\n\n## 代理服务\n\n当前使用的代理服务：\n- **主要服务**: githubproxy.cc (测试有效，加速约 3 倍)\n- **备用服务**: ghfast.top\n\n## 使用方法\n\n### 1. Git Clone 加速\n\n将 GitHub 仓库链接前加上 `https://githubproxy.cc/` 前缀：\n\n```bash\n# 原始链接\ngit clone https://github.com/username/repo.git\n\n# 加速链接\ngit clone https://githubproxy.cc/https://github.com/username/repo.git\n```\n\n### 2. 文件下载加速\n\n支持以下类型的 GitHub 资源加速：\n\n- **Raw 文件**: `https://raw.githubusercontent.com/...`\n- **Release 文件**: 项目发布的附件\n- **Archive 压缩包**: 仓库打包下载\n- **Gist 文件**: `gist.github.com` 或 `gist.githubusercontent.com`\n\n```bash\n# 原始链接\nwget https://raw.githubusercontent.com/username/repo/main/file.txt\n\n# 加速链接\nwget https://githubproxy.cc/https://raw.githubusercontent.com/username/repo/main/file.txt\n```\n\n### 3. 使用辅助脚本\n\n使用 `scripts/convert_url.py` 自动转换 GitHub 链接：\n\n```bash\npython scripts/convert_url.py \"https://github.com/username/repo.git\"\n```\n\n## 链接转换规则\n\n| 原始链接格式 | 转换后格式 |\n|-------------|-----------|\n| `https://github.com/username/repo.git` | `https://githubproxy.cc/https://github.com/username/repo.git` |\n| `https://raw.githubusercontent.com/...` | `https://githubproxy.cc/https://raw.githubusercontent.com/...` |\n| `https://github.com/.../releases/download/...` | `https://githubproxy.cc/https://github.com/.../releases/download/...` |\n| `https://github.com/.../archive/...` | `https://githubproxy.cc/https://github.com/.../archive/...` |\n\n## 注意事项\n\n- 本服务仅供学习研究使用，请勿滥用\n- 如果 githubproxy.cc 不可用，请尝试备用服务 ghfast.top\n- 不支持 SSH Key 方式的 git clone\n- Push、PR、Issue 等操作建议直接使用官方 GitHub 地址\n","frontmatter":{"name":"github-proxy","description":"GitHub 国内访问加速 skill，使用 githubproxy.cc 代理加速 GitHub 仓库克隆、文件下载、Raw 文件访问等操作。使用场景：(1) 需要 git clone GitHub 仓库时加速，(2) 下载 GitHub Release 文件、Raw 文件、Archive 压缩包时加速，(3) 任何需要访问 GitHub 资源但速度慢的场景"},"isInternal":false,"tokens":711,"sizeBytes":2353},{"name":"SKILL.md","path":"bot/workspace/skills/github/SKILL.md","rawUrl":"https://raw.githubusercontent.com/volcengine/OpenViking/HEAD/bot/workspace/skills/github/SKILL.md","title":"github","category":"anthropic-skill","format":"markdown","content":"---\nname: github\ndescription: \"Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.\"\nmetadata: {\"vikingbot\":{\"emoji\":\"🐙\",\"requires\":{\"bins\":[\"gh\"]},\"install\":[{\"id\":\"brew\",\"kind\":\"brew\",\"formula\":\"gh\",\"bins\":[\"gh\"],\"label\":\"Install GitHub CLI (brew)\"},{\"id\":\"apt\",\"kind\":\"apt\",\"package\":\"gh\",\"bins\":[\"gh\"],\"label\":\"Install GitHub CLI (apt)\"}]}}\n---\n\n# GitHub Skill\n\nUse the `gh` CLI to interact with GitHub. Always specify `--repo owner/repo` when not in a git directory, or use URLs directly.\n\n## Pull Requests\n\nCheck CI status on a PR:\n```bash\ngh pr checks 55 --repo owner/repo\n```\n\nList recent workflow runs:\n```bash\ngh run list --repo owner/repo --limit 10\n```\n\nView a run and see which steps failed:\n```bash\ngh run view <run-id> --repo owner/repo\n```\n\nView logs for failed steps only:\n```bash\ngh run view <run-id> --repo owner/repo --log-failed\n```\n\n## API for Advanced Queries\n\nThe `gh api` command is useful for accessing data not available through other subcommands.\n\nGet PR with specific fields:\n```bash\ngh api repos/owner/repo/pulls/55 --jq '.title, .state, .user.login'\n```\n\n## JSON Output\n\nMost commands support `--json` for structured output.  You can use `--jq` to filter:\n\n```bash\ngh issue list --repo owner/repo --json number,title --jq '.[] | \"\\(.number): \\(.title)\"'\n```\n","frontmatter":{"name":"github","description":"Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.","metadata":{"vikingbot":{"emoji":"🐙","requires":{"bins":["gh"]},"install":[{"id":"brew","kind":"brew","formula":"gh","bins":["gh"],"label":"Install GitHub CLI (brew)"},{"id":"apt","kind":"apt","package":"gh","bins":["gh"],"label":"Install GitHub CLI (apt)"}]}}},"isInternal":false,"tokens":382,"sizeBytes":1376},{"name":"SKILL.md","path":"bot/workspace/skills/opencode/SKILL.md","rawUrl":"https://raw.githubusercontent.com/volcengine/OpenViking/HEAD/bot/workspace/skills/opencode/SKILL.md","title":"opencode","category":"anthropic-skill","format":"markdown","content":"---\nname: opencode\ndescription: Monitor and manage your OpenCode tasks using helper scripts.\nmetadata: {\"vikingbot\":{\"emoji\":\"💻\",\"requires\":{\"bins\":[\"python3\"]}}}\n---\n\n# OpenCode Skill\n\nUse helper scripts to manage your OpenCode instances.\n\n## Helper Scripts\n\nAll scripts are in the workspace skills directory. Run them with:\n```bash\nuv run python skills/opencode/script_name.py\n```\n\nNote: Do not kill the opencode process.\n\n## Scripts\n\n### `list_sessions.py` \nListing all OpenCode sessions\n\nExample:\nuv run python skills/opencode/list_sessions.py\n\n\n### `list_messages_of_session.py`\nListing latest OpenCode messages by session_id \n\nExample:\nuv run python skills/opencode/list_sessions.py {session_id}\n\n## Session Status Types\n\n- **🟢 WAITING**: Last message was from user - agent is waiting for input\n- **🔴 WORKING**: Last message was from assistant - agent recently finished or may be working\n- **🟡 UNKNOWN**: Cannot determine status\n\n","frontmatter":{"name":"opencode","description":"Monitor and manage your OpenCode tasks using helper scripts.","metadata":{"vikingbot":{"emoji":"💻","requires":{"bins":["python3"]}}}},"isInternal":false,"tokens":216,"sizeBytes":948},{"name":"status.json","path":"bot/workspace/skills/opencode/status.json","rawUrl":"https://raw.githubusercontent.com/volcengine/OpenViking/HEAD/bot/workspace/skills/opencode/status.json","title":"Opencode Skill","category":"anthropic-skill","format":"json","content":"# Opencode Skill\n\nPath: `bot/workspace/skills/opencode/status.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/volcengine/OpenViking/HEAD/bot/workspace/skills/opencode/status.json)","tokens":300,"sizeBytes":201},{"name":"README.md","path":"bot/workspace/skills/README.md","rawUrl":"https://raw.githubusercontent.com/volcengine/OpenViking/HEAD/bot/workspace/skills/README.md","title":"Skills Skill","category":"anthropic-skill","format":"markdown","content":"# Skills Skill\n\nPath: `bot/workspace/skills/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/volcengine/OpenViking/HEAD/bot/workspace/skills/README.md)","tokens":300,"sizeBytes":177},{"name":"SKILL.md","path":"bot/workspace/skills/skill-creator/SKILL.md","rawUrl":"https://raw.githubusercontent.com/volcengine/OpenViking/HEAD/bot/workspace/skills/skill-creator/SKILL.md","title":"skill-creator","category":"anthropic-skill","format":"markdown","content":"---\nname: skill-creator\ndescription: Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.\n---\n\n# Skill Creator\n\nThis skill provides guidance for creating effective skills.\n\n## About Skills\n\nSkills are modular, self-contained packages that extend the agent's capabilities by providing\nspecialized knowledge, workflows, and tools. Think of them as \"onboarding guides\" for specific\ndomains or tasks—they transform the agent from a general-purpose agent into a specialized agent\nequipped with procedural knowledge that no model can fully possess.\n\n### What Skills Provide\n\n1. Specialized workflows - Multi-step procedures for specific domains\n2. Tool integrations - Instructions for working with specific file formats or APIs\n3. Domain expertise - Company-specific knowledge, schemas, business logic\n4. Bundled resources - Scripts, references, and assets for complex and repetitive tasks\n\n## Core Principles\n\n### Concise is Key\n\nThe context window is a public good. Skills share the context window with everything else the agent needs: system prompt, conversation history, other Skills' metadata, and the actual user request.\n\n**Default assumption: the agent is already very smart.** Only add context the agent doesn't already have. Challenge each piece of information: \"Does the agent really need this explanation?\" and \"Does this paragraph justify its token cost?\"\n\nPrefer concise examples over verbose explanations.\n\n### Set Appropriate Degrees of Freedom\n\nMatch the level of specificity to the task's fragility and variability:\n\n**High freedom (text-based instructions)**: Use when multiple approaches are valid, decisions depend on context, or heuristics guide the approach.\n\n**Medium freedom (pseudocode or scripts with parameters)**: Use when a preferred pattern exists, some variation is acceptable, or configuration affects behavior.\n\n**Low freedom (specific scripts, few parameters)**: Use when operations are fragile and error-prone, consistency is critical, or a specific sequence must be followed.\n\nThink of the agent as exploring a path: a narrow bridge with cliffs needs specific guardrails (low freedom), while an open field allows many routes (high freedom).\n\n### Anatomy of a Skill\n\nEvery skill consists of a required SKILL.md file and optional bundled resources:\n\n```\nskill-name/\n├── SKILL.md (required)\n│   ├── YAML frontmatter metadata (required)\n│   │   ├── name: (required)\n│   │   └── description: (required)\n│   └── Markdown instructions (required)\n└── Bundled Resources (optional)\n    ├── scripts/          - Executable code (Python/Bash/etc.)\n    ├── references/       - Documentation intended to be loaded into context as needed\n    └── assets/           - Files used in output (templates, icons, fonts, etc.)\n```\n\n#### SKILL.md (required)\n\nEvery SKILL.md consists of:\n\n- **Frontmatter** (YAML): Contains `name` and `description` fields. These are the only fields that the agent reads to determine when the skill gets used, thus it is very important to be clear and comprehensive in describing what the skill is, and when it should be used.\n- **Body** (Markdown): Instructions and guidance for using the skill. Only loaded AFTER the skill triggers (if at all).\n\n#### Bundled Resources (optional)\n\n##### Scripts (`scripts/`)\n\nExecutable code (Python/Bash/etc.) for tasks that require deterministic reliability or are repeatedly rewritten.\n\n- **When to include**: When the same code is being rewritten repeatedly or deterministic reliability is needed\n- **Example**: `scripts/rotate_pdf.py` for PDF rotation tasks\n- **Benefits**: Token efficient, deterministic, may be executed without loading into context\n- **Note**: Scripts may still need to be read by the agent for patching or environment-specific adjustments\n\n##### References (`references/`)\n\nDocumentation and reference material intended to be loaded as needed into context to inform the agent's process and thinking.\n\n- **When to include**: For documentation that the agent should reference while working\n- **Examples**: `references/finance.md` for financial schemas, `references/mnda.md` for company NDA template, `references/policies.md` for company policies, `references/api_docs.md` for API specifications\n- **Use cases**: Database schemas, API documentation, domain knowledge, company policies, detailed workflow guides\n- **Benefits**: Keeps SKILL.md lean, loaded only when the agent determines it's needed\n- **Best practice**: If files are large (>10k words), include grep search patterns in SKILL.md\n- **Avoid duplication**: Information should live in either SKILL.md or references files, not both. Prefer references files for detailed information unless it's truly core to the skill—this keeps SKILL.md lean while making information discoverable without hogging the context window. Keep only essential procedural instructions and workflow guidance in SKILL.md; move detailed reference material, schemas, and examples to references files.\n\n##### Assets (`assets/`)\n\nFiles not intended to be loaded into context, but rather used within the output the agent produces.\n\n- **When to include**: When the skill needs files that will be used in the final output\n- **Examples**: `assets/logo.png` for brand assets, `assets/slides.pptx` for PowerPoint templates, `assets/frontend-template/` for HTML/React boilerplate, `assets/font.ttf` for typography\n- **Use cases**: Templates, images, icons, boilerplate code, fonts, sample documents that get copied or modified\n- **Benefits**: Separates output resources from documentation, enables the agent to use files without loading them into context\n\n#### What to Not Include in a Skill\n\nA skill should only contain essential files that directly support its functionality. Do NOT create extraneous documentation or auxiliary files, including:\n\n- README.md\n- INSTALLATION_GUIDE.md\n- QUICK_REFERENCE.md\n- CHANGELOG.md\n- etc.\n\nThe skill should only contain the information needed for an AI agent to do the job at hand. It should not contain auxiliary context about the process that went into creating it, setup and testing procedures, user-facing documentation, etc. Creating additional documentation files just adds clutter and confusion.\n\n### Progressive Disclosure Design Principle\n\nSkills use a three-level loading system to manage context efficiently:\n\n1. **Metadata (name + description)** - Always in context (~100 words)\n2. **SKILL.md body** - When skill triggers (<5k words)\n3. **Bundled resources** - As needed by the agent (Unlimited because scripts can be executed without reading into context window)\n\n#### Progressive Disclosure Patterns\n\nKeep SKILL.md body to the essentials and under 500 lines to minimize context bloat. Split content into separate files when approaching this limit. When splitting out content into other files, it is very important to reference them from SKILL.md and describe clearly when to read them, to ensure the reader of the skill knows they exist and when to use them.\n\n**Key principle:** When a skill supports multiple variations, frameworks, or options, keep only the core workflow and selection guidance in SKILL.md. Move variant-specific details (patterns, examples, configuration) into separate reference files.\n\n**Pattern 1: High-level guide with references**\n\n```markdown\n# PDF Processing\n\n## Quick start\n\nExtract text with pdfplumber:\n[code example]\n\n## Advanced features\n\n- **Form filling**: See [FORMS.md](FORMS.md) for complete guide\n- **API reference**: See [REFERENCE.md](REFERENCE.md) for all methods\n- **Examples**: See [EXAMPLES.md](EXAMPLES.md) for common patterns\n```\n\nthe agent loads FORMS.md, REFERENCE.md, or EXAMPLES.md only when needed.\n\n**Pattern 2: Domain-specific organization**\n\nFor Skills with multiple domains, organize content by domain to avoid loading irrelevant context:\n\n```\nbigquery-skill/\n├── SKILL.md (overview and navigation)\n└── reference/\n    ├── finance.md (revenue, billing metrics)\n    ├── sales.md (opportunities, pipeline)\n    ├── product.md (API usage, features)\n    └── marketing.md (campaigns, attribution)\n```\n\nWhen a user asks about sales metrics, the agent only reads sales.md.\n\nSimilarly, for skills supporting multiple frameworks or variants, organize by variant:\n\n```\ncloud-deploy/\n├── SKILL.md (workflow + provider selection)\n└── references/\n    ├── aws.md (AWS deployment patterns)\n    ├── gcp.md (GCP deployment patterns)\n    └── azure.md (Azure deployment patterns)\n```\n\nWhen the user chooses AWS, the agent only reads aws.md.\n\n**Pattern 3: Conditional details**\n\nShow basic content, link to advanced content:\n\n```markdown\n# DOCX Processing\n\n## Creating documents\n\nUse docx-js for new documents. See [DOCX-JS.md](DOCX-JS.md).\n\n## Editing documents\n\nFor simple edits, modify the XML directly.\n\n**For tracked changes**: See [REDLINING.md](REDLINING.md)\n**For OOXML details**: See [OOXML.md](OOXML.md)\n```\n\nthe agent reads REDLINING.md or OOXML.md only when the user needs those features.\n\n**Important guidelines:**\n\n- **Avoid deeply nested references** - Keep references one level deep from SKILL.md. All reference files should link directly from SKILL.md.\n- **Structure longer reference files** - For files longer than 100 lines, include a table of contents at the top so the agent can see the full scope when previewing.\n\n## Skill Creation Process\n\nSkill creation involves these steps:\n\n1. Understand the skill with concrete examples\n2. Plan reusable skill contents (scripts, references, assets)\n3. Initialize the skill (run init_skill.py)\n4. Edit the skill (implement resources and write SKILL.md)\n5. Package the skill (run package_skill.py)\n6. Iterate based on real usage\n\nFollow these steps in order, skipping only if there is a clear reason why they are not applicable.\n\n### Skill Naming\n\n- Use lowercase letters, digits, and hyphens only; normalize user-provided titles to hyphen-case (e.g., \"Plan Mode\" -> `plan-mode`).\n- When generating names, generate a name under 64 characters (letters, digits, hyphens).\n- Prefer short, verb-led phrases that describe the action.\n- Namespace by tool when it improves clarity or triggering (e.g., `gh-address-comments`, `linear-address-issue`).\n- Name the skill folder exactly after the skill name.\n\n### Step 1: Understanding the Skill with Concrete Examples\n\nSkip this step only when the skill's usage patterns are already clearly understood. It remains valuable even when working with an existing skill.\n\nTo create an effective skill, clearly understand concrete examples of how the skill will be used. This understanding can come from either direct user examples or generated examples that are validated with user feedback.\n\nFor example, when building an image-editor skill, relevant questions include:\n\n- \"What functionality should the image-editor skill support? Editing, rotating, anything else?\"\n- \"Can you give some examples of how this skill would be used?\"\n- \"I can imagine users asking for things like 'Remove the red-eye from this image' or 'Rotate this image'. Are there other ways you imagine this skill being used?\"\n- \"What would a user say that should trigger this skill?\"\n\nTo avoid overwhelming users, avoid asking too many questions in a single message. Start with the most important questions and follow up as needed for better effectiveness.\n\nConclude this step when there is a clear sense of the functionality the skill should support.\n\n### Step 2: Planning the Reusable Skill Contents\n\nTo turn concrete examples into an effective skill, analyze each example by:\n\n1. Considering how to execute on the example from scratch\n2. Identifying what scripts, references, and assets would be helpful when executing these workflows repeatedly\n\nExample: When building a `pdf-editor` skill to handle queries like \"Help me rotate this PDF,\" the analysis shows:\n\n1. Rotating a PDF requires re-writing the same code each time\n2. A `scripts/rotate_pdf.py` script would be helpful to store in the skill\n\nExample: When designing a `frontend-webapp-builder` skill for queries like \"Build me a todo app\" or \"Build me a dashboard to track my steps,\" the analysis shows:\n\n1. Writing a frontend webapp requires the same boilerplate HTML/React each time\n2. An `assets/hello-world/` template containing the boilerplate HTML/React project files would be helpful to store in the skill\n\nExample: When building a `big-query` skill to handle queries like \"How many users have logged in today?\" the analysis shows:\n\n1. Querying BigQuery requires re-discovering the table schemas and relationships each time\n2. A `references/schema.md` file documenting the table schemas would be helpful to store in the skill\n\nTo establish the skill's contents, analyze each concrete example to create a list of the reusable resources to include: scripts, references, and assets.\n\n### Step 3: Initializing the Skill\n\nAt this point, it is time to actually create the skill.\n\nSkip this step only if the skill being developed already exists, and iteration or packaging is needed. In this case, continue to the next step.\n\nWhen creating a new skill from scratch, always run the `init_skill.py` script. The script conveniently generates a new template skill directory that automatically includes everything a skill requires, making the skill creation process much more efficient and reliable.\n\nUsage:\n\n```bash\nscripts/init_skill.py <skill-name> --path <output-directory> [--resources scripts,references,assets] [--examples]\n```\n\nExamples:\n\n```bash\nscripts/init_skill.py my-skill --path skills/public\nscripts/init_skill.py my-skill --path skills/public --resources scripts,references\nscripts/init_skill.py my-skill --path skills/public --resources scripts --examples\n```\n\nThe script:\n\n- Creates the skill directory at the specified path\n- Generates a SKILL.md template with proper frontmatter and TODO placeholders\n- Optionally creates resource directories based on `--resources`\n- Optionally adds example files when `--examples` is set\n\nAfter initialization, customize the SKILL.md and add resources as needed. If you used `--examples`, replace or delete placeholder files.\n\n### Step 4: Edit the Skill\n\nWhen editing the (newly-generated or existing) skill, remember that the skill is being created for another instance of the agent to use. Include information that would be beneficial and non-obvious to the agent. Consider what procedural knowledge, domain-specific details, or reusable assets would help another the agent instance execute these tasks more effectively.\n\n#### Learn Proven Design Patterns\n\nConsult these helpful guides based on your skill's needs:\n\n- **Multi-step processes**: See references/workflows.md for sequential workflows and conditional logic\n- **Specific output formats or quality standards**: See references/output-patterns.md for template and example patterns\n\nThese files contain established best practices for effective skill design.\n\n#### Start with Reusable Skill Contents\n\nTo begin implementation, start with the reusable resources identified above: `scripts/`, `references/`, and `assets/` files. Note that this step may require user input. For example, when implementing a `brand-guidelines` skill, the user may need to provide brand assets or templates to store in `assets/`, or documentation to store in `references/`.\n\nAdded scripts must be tested by actually running them to ensure there are no bugs and that the output matches what is expected. If there are many similar scripts, only a representative sample needs to be tested to ensure confidence that they all work while balancing time to completion.\n\nIf you used `--examples`, delete any placeholder files that are not needed for the skill. Only create resource directories that are actually required.\n\n#### Update SKILL.md\n\n**Writing Guidelines:** Always use imperative/infinitive form.\n\n##### Frontmatter\n\nWrite the YAML frontmatter with `name` and `description`:\n\n- `name`: The skill name\n- `description`: This is the primary triggering mechanism for your skill, and helps the agent understand when to use the skill.\n  - Include both what the Skill does and specific triggers/contexts for when to use it.\n  - Include all \"when to use\" information here - Not in the body. The body is only loaded after triggering, so \"When to Use This Skill\" sections in the body are not helpful to the agent.\n  - Example description for a `docx` skill: \"Comprehensive document creation, editing, and analysis with support for tracked changes, comments, formatting preservation, and text extraction. Use when the agent needs to work with professional documents (.docx files) for: (1) Creating new documents, (2) Modifying or editing content, (3) Working with tracked changes, (4) Adding comments, or any other document tasks\"\n\nDo not include any other fields in YAML frontmatter.\n\n##### Body\n\nWrite instructions for using the skill and its bundled resources.\n\n### Step 5: Packaging a Skill\n\nOnce development of the skill is complete, it must be packaged into a distributable .skill file that gets shared with the user. The packaging process automatically validates the skill first to ensure it meets all requirements:\n\n```bash\nscripts/package_skill.py <path/to/skill-folder>\n```\n\nOptional output directory specification:\n\n```bash\nscripts/package_skill.py <path/to/skill-folder> ./dist\n```\n\nThe packaging script will:\n\n1. **Validate** the skill automatically, checking:\n\n   - YAML frontmatter format and required fields\n   - Skill naming conventions and directory structure\n   - Description completeness and quality\n   - File organization and resource references\n\n2. **Package** the skill if validation passes, creating a .skill file named after the skill (e.g., `my-skill.skill`) that includes all files and maintains the proper directory structure for distribution. The .skill file is a zip file with a .skill extension.\n\nIf validation fails, the script will report the errors and exit without creating a package. Fix any validation errors and run the packaging command again.\n\n### Step 6: Iterate\n\nAfter testing the skill, users may request improvements. Often this happens right after using the skill, with fresh context of how the skill performed.\n\n**Iteration workflow:**\n\n1. Use the skill on real tasks\n2. Notice struggles or inefficiencies\n3. Identify how SKILL.md or bundled resources should be updated\n4. Implement changes and test again\n","frontmatter":{"name":"skill-creator","description":"Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets."},"isInternal":false,"tokens":3801,"sizeBytes":18460},{"name":"SKILL.md","path":"bot/workspace/skills/summarize/SKILL.md","rawUrl":"https://raw.githubusercontent.com/volcengine/OpenViking/HEAD/bot/workspace/skills/summarize/SKILL.md","title":"summarize","category":"anthropic-skill","format":"markdown","content":"---\nname: summarize\ndescription: Summarize or extract text/transcripts from URLs, podcasts, and local files (great fallback for “transcribe this YouTube/video”).\nhomepage: https://summarize.sh\nmetadata: {\"vikingbot\":{\"emoji\":\"🧾\",\"requires\":{\"bins\":[\"summarize\"]},\"install\":[{\"id\":\"brew\",\"kind\":\"brew\",\"formula\":\"steipete/tap/summarize\",\"bins\":[\"summarize\"],\"label\":\"Install summarize (brew)\"}]}}\n---\n\n# Summarize\n\nFast CLI to summarize URLs, local files, and YouTube links.\n\n## When to use (trigger phrases)\n\nUse this skill immediately when the user asks any of:\n- “use summarize.sh”\n- “what’s this link/video about?”\n- “summarize this URL/article”\n- “transcribe this YouTube/video” (best-effort transcript extraction; no `yt-dlp` needed)\n\n## Quick start\n\n```bash\nsummarize \"https://example.com\" --model google/gemini-3-flash-preview\nsummarize \"/path/to/file.pdf\" --model google/gemini-3-flash-preview\nsummarize \"https://youtu.be/dQw4w9WgXcQ\" --youtube auto\n```\n\n## YouTube: summary vs transcript\n\nBest-effort transcript (URLs only):\n\n```bash\nsummarize \"https://youtu.be/dQw4w9WgXcQ\" --youtube auto --extract-only\n```\n\nIf the user asked for a transcript but it’s huge, return a tight summary first, then ask which section/time range to expand.\n\n## Model + keys\n\nSet the API key for your chosen provider:\n- OpenAI: `OPENAI_API_KEY`\n- Anthropic: `ANTHROPIC_API_KEY`\n- xAI: `XAI_API_KEY`\n- Google: `GEMINI_API_KEY` (aliases: `GOOGLE_GENERATIVE_AI_API_KEY`, `GOOGLE_API_KEY`)\n\nDefault model is `google/gemini-3-flash-preview` if none is set.\n\n## Useful flags\n\n- `--length short|medium|long|xl|xxl|<chars>`\n- `--max-output-tokens <count>`\n- `--extract-only` (URLs only)\n- `--json` (machine readable)\n- `--firecrawl auto|off|always` (fallback extraction)\n- `--youtube auto` (Apify fallback if `APIFY_API_TOKEN` set)\n\n## Config\n\nOptional config file: `~/.summarize/config.json`\n\n```json\n{ \"model\": \"openai/gpt-5.2\" }\n```\n\nOptional services:\n- `FIRECRAWL_API_KEY` for blocked sites\n- `APIFY_API_TOKEN` for YouTube fallback\n","frontmatter":{"name":"summarize","description":"Summarize or extract text/transcripts from URLs, podcasts, and local files (great fallback for “transcribe this YouTube/video”).","homepage":"https://summarize.sh","metadata":{"vikingbot":{"emoji":"🧾","requires":{"bins":["summarize"]},"install":[{"id":"brew","kind":"brew","formula":"steipete/tap/summarize","bins":["summarize"],"label":"Install summarize (brew)"}]}}},"isInternal":false,"tokens":581,"sizeBytes":2042},{"name":"SKILL.md","path":"bot/workspace/skills/tmux/SKILL.md","rawUrl":"https://raw.githubusercontent.com/volcengine/OpenViking/HEAD/bot/workspace/skills/tmux/SKILL.md","title":"tmux","category":"anthropic-skill","format":"markdown","content":"---\nname: tmux\ndescription: Remote-control tmux sessions for interactive CLIs by sending keystrokes and scraping pane output.\nmetadata: {\"vikingbot\":{\"emoji\":\"🧵\",\"os\":[\"darwin\",\"linux\"],\"requires\":{\"bins\":[\"tmux\"]}}}\n---\n\n# tmux Skill\n\nUse tmux only when you need an interactive TTY. Prefer exec background mode for long-running, non-interactive tasks.\n\n## Quickstart (isolated socket, exec tool)\n\n```bash\nSOCKET_DIR=\"${NANOBOT_TMUX_SOCKET_DIR:-${TMPDIR:-/tmp}/vikingbot-tmux-sockets}\"\nmkdir -p \"$SOCKET_DIR\"\nSOCKET=\"$SOCKET_DIR/vikingbot.sock\"\nSESSION=vikingbot-python\n\ntmux -S \"$SOCKET\" new -d -s \"$SESSION\" -n shell\ntmux -S \"$SOCKET\" send-keys -t \"$SESSION\":0.0 -- 'PYTHON_BASIC_REPL=1 python3 -q' Enter\ntmux -S \"$SOCKET\" capture-pane -p -J -t \"$SESSION\":0.0 -S -200\n```\n\nAfter starting a session, always print monitor commands:\n\n```\nTo monitor:\n  tmux -S \"$SOCKET\" attach -t \"$SESSION\"\n  tmux -S \"$SOCKET\" capture-pane -p -J -t \"$SESSION\":0.0 -S -200\n```\n\n## Socket convention\n\n- Use `NANOBOT_TMUX_SOCKET_DIR` environment variable.\n- Default socket path: `\"$NANOBOT_TMUX_SOCKET_DIR/vikingbot.sock\"`.\n\n## Targeting panes and naming\n\n- Target format: `session:window.pane` (defaults to `:0.0`).\n- Keep names short; avoid spaces.\n- Inspect: `tmux -S \"$SOCKET\" list-sessions`, `tmux -S \"$SOCKET\" list-panes -a`.\n\n## Finding sessions\n\n- List sessions on your socket: `{baseDir}/scripts/find-sessions.sh -S \"$SOCKET\"`.\n- Scan all sockets: `{baseDir}/scripts/find-sessions.sh --all` (uses `NANOBOT_TMUX_SOCKET_DIR`).\n\n## Sending input safely\n\n- Prefer literal sends: `tmux -S \"$SOCKET\" send-keys -t target -l -- \"$cmd\"`.\n- Control keys: `tmux -S \"$SOCKET\" send-keys -t target C-c`.\n\n## Watching output\n\n- Capture recent history: `tmux -S \"$SOCKET\" capture-pane -p -J -t target -S -200`.\n- Wait for prompts: `{baseDir}/scripts/wait-for-text.sh -t session:0.0 -p 'pattern'`.\n- Attaching is OK; detach with `Ctrl+b d`.\n\n## Spawning processes\n\n- For python REPLs, set `PYTHON_BASIC_REPL=1` (non-basic REPL breaks send-keys flows).\n\n## Windows / WSL\n\n- tmux is supported on macOS/Linux. On Windows, use WSL and install tmux inside WSL.\n- This skill is gated to `darwin`/`linux` and requires `tmux` on PATH.\n\n## Orchestrating Coding Agents (Codex, Claude Code)\n\ntmux excels at running multiple coding agents in parallel:\n\n```bash\nSOCKET=\"${TMPDIR:-/tmp}/codex-army.sock\"\n\n# Create multiple sessions\nfor i in 1 2 3 4 5; do\n  tmux -S \"$SOCKET\" new-session -d -s \"agent-$i\"\ndone\n\n# Launch agents in different workdirs\ntmux -S \"$SOCKET\" send-keys -t agent-1 \"cd /tmp/project1 && codex --yolo 'Fix bug X'\" Enter\ntmux -S \"$SOCKET\" send-keys -t agent-2 \"cd /tmp/project2 && codex --yolo 'Fix bug Y'\" Enter\n\n# Poll for completion (check if prompt returned)\nfor sess in agent-1 agent-2; do\n  if tmux -S \"$SOCKET\" capture-pane -p -t \"$sess\" -S -3 | grep -q \"❯\"; then\n    echo \"$sess: DONE\"\n  else\n    echo \"$sess: Running...\"\n  fi\ndone\n\n# Get full output from completed session\ntmux -S \"$SOCKET\" capture-pane -p -t agent-1 -S -500\n```\n\n**Tips:**\n- Use separate git worktrees for parallel fixes (no branch conflicts)\n- `pnpm install` first before running codex in fresh clones\n- Check for shell prompt (`❯` or `$`) to detect completion\n- Codex needs `--yolo` or `--full-auto` for non-interactive fixes\n\n## Cleanup\n\n- Kill a session: `tmux -S \"$SOCKET\" kill-session -t \"$SESSION\"`.\n- Kill all sessions on a socket: `tmux -S \"$SOCKET\" list-sessions -F '#{session_name}' | xargs -r -n1 tmux -S \"$SOCKET\" kill-session -t`.\n- Remove everything on the private socket: `tmux -S \"$SOCKET\" kill-server`.\n\n## Helper: wait-for-text.sh\n\n`{baseDir}/scripts/wait-for-text.sh` polls a pane for a regex (or fixed string) with a timeout.\n\n```bash\n{baseDir}/scripts/wait-for-text.sh -t session:0.0 -p 'pattern' [-F] [-T 20] [-i 0.5] [-l 2000]\n```\n\n- `-t`/`--target` pane target (required)\n- `-p`/`--pattern` regex to match (required); add `-F` for fixed string\n- `-T` timeout seconds (integer, default 15)\n- `-i` poll interval seconds (default 0.5)\n- `-l` history lines to search (integer, default 1000)\n","frontmatter":{"name":"tmux","description":"Remote-control tmux sessions for interactive CLIs by sending keystrokes and scraping pane output.","metadata":{"vikingbot":{"emoji":"🧵","os":["darwin","linux"],"requires":{"bins":["tmux"]}}}},"isInternal":false,"tokens":1259,"sizeBytes":4073},{"name":"SKILL.md","path":"bot/workspace/skills/weather/SKILL.md","rawUrl":"https://raw.githubusercontent.com/volcengine/OpenViking/HEAD/bot/workspace/skills/weather/SKILL.md","title":"weather","category":"anthropic-skill","format":"markdown","content":"---\nname: weather\ndescription: Get current weather and forecasts (no API key required).\nhomepage: https://wttr.in/:help\nmetadata: {\"vikingbot\":{\"emoji\":\"🌤️\",\"requires\":{\"bins\":[\"curl\"]}}}\n---\n\n# Weather\n\nTwo free services, no API keys needed.\n\n## wttr.in (primary)\n\nQuick one-liner:\n```bash\ncurl -s \"wttr.in/London?format=3\"\n# Output: London: ⛅️ +8°C\n```\n\nCompact format:\n```bash\ncurl -s \"wttr.in/London?format=%l:+%c+%t+%h+%w\"\n# Output: London: ⛅️ +8°C 71% ↙5km/h\n```\n\nFull forecast:\n```bash\ncurl -s \"wttr.in/London?T\"\n```\n\nFormat codes: `%c` condition · `%t` temp · `%h` humidity · `%w` wind · `%l` location · `%m` moon\n\nTips:\n- URL-encode spaces: `wttr.in/New+York`\n- Airport codes: `wttr.in/JFK`\n- Units: `?m` (metric) `?u` (USCS)\n- Today only: `?1` · Current only: `?0`\n- PNG: `curl -s \"wttr.in/Berlin.png\" -o /tmp/weather.png`\n\n## Open-Meteo (fallback, JSON)\n\nFree, no key, good for programmatic use:\n```bash\ncurl -s \"https://api.open-meteo.com/v1/forecast?latitude=51.5&longitude=-0.12&current_weather=true\"\n```\n\nFind coordinates for a city, then query. Returns JSON with temp, windspeed, weathercode.\n\nDocs: https://open-meteo.com/en/docs\n","frontmatter":{"name":"weather","description":"Get current weather and forecasts (no API key required).","homepage":"https://wttr.in/:help","metadata":{"vikingbot":{"emoji":"🌤️","requires":{"bins":["curl"]}}}},"isInternal":false,"tokens":387,"sizeBytes":1170},{"name":"SKILL.md","path":"examples/openclaw-plugin/skills/install-openviking-memory/SKILL.md","rawUrl":"https://raw.githubusercontent.com/volcengine/OpenViking/HEAD/examples/openclaw-plugin/skills/install-openviking-memory/SKILL.md","title":"install-openviking-memory","category":"anthropic-skill","format":"markdown","content":"---\nname: install-openviking-memory\ndescription: >\n  Install and configure the OpenViking long-term memory plugin for OpenClaw via natural conversation.\n  Once installed, the plugin automatically captures facts from chats and recalls relevant context\n  before each reply (auto-capture + auto-recall, cross-session). Covers prerequisites, install through\n  OpenClaw's plugin manager (ov-install as backup), wizard-based configuration, slot activation,\n  gateway restart, verification, multi-tenant root keys, multi-instance and uninstall. The user needs\n  no CLI knowledge — the agent runs everything and only asks for a few values. Assumes the OpenViking\n  server is already running; it does NOT install the server. Trigger on: \"install OpenViking\",\n  \"set up memory\", \"configure memory plugin\", \"add long-term memory\", \"semantic memory\", \"RAG\",\n  \"帮我装 OpenViking\", \"配置记忆插件\", \"安装记忆功能\", \"接入 OpenViking\".\nversion: 2026.6.5\nmetadata:\n  openclaw:\n    requires:\n      bins:\n        - node\n        - openclaw\n  emoji: \"🦣\"\n  homepage: \"https://github.com/volcengine/OpenViking\"\ntags:\n  - install\n  - setup\n  - memory\n  - openviking\n  - rag\n  - long-term-memory\n---\n\n# Install OpenViking Memory — Agent Installation & Reference Skill\n\nYou are about to help a user install and configure the OpenViking memory plugin for OpenClaw.\nThe 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.\n\n**Read this entire document before starting. Follow every step in order. Do NOT skip steps.**\n\nThis skill is split into two halves:\n\n- **Part 1 (STEP 0–10): Natural-language installation walkthrough.** Use this on first install or reconfigure.\n- **Part 2: Reference.** Tools, config schema, multi-tenant, multi-instance, daily ops, uninstall, error recovery. Read on demand.\n\n> **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.\n\n---\n\n# Part 1 — Natural-Language Installation Walkthrough\n\n## STEP 0: Detect Language\n\nIf the user's first message contains Chinese characters, respond in **Chinese** throughout.\nOtherwise respond in **English**.\nAll user-facing messages below have (CN)/(EN) variants — use the matching one.\n\nDo NOT show this step to the user.\n\n---\n\n## STEP 1: Detect Operating System (silently)\n\nRun this command and remember the result. Do NOT show it to the user.\n\n```bash\nuname -s 2>/dev/null || echo WINDOWS\n```\n\n- Output contains `Darwin` → `OS=mac`\n- Output contains `Linux` → `OS=linux`\n- Output is `WINDOWS` or the command fails → `OS=windows`\n\n---\n\n## STEP 2: Check Prerequisites (silently)\n\nRun silently:\n\n```bash\nnode -v\nopenclaw --version\n```\n\n**If `node` is missing:**\n\n> (CN) 你的系统没有安装 Node.js。OpenClaw 和 OpenViking 插件需要 Node.js >= 22。请先安装 Node.js，然后再回来找我。\n> (EN) Node.js is not installed. OpenClaw and the OpenViking plugin require Node.js >= 22. Please install Node.js first, then come back.\n\n**Stop. Do NOT continue.**\n\n**If `openclaw` is missing:**\n\n> (CN) 你的系统没有安装 OpenClaw。请先安装 OpenClaw（>= 2026.5.27），然后再回来找我。\n> (EN) OpenClaw is not installed. Please install OpenClaw (>= 2026.5.27) first, then come back.\n\n**Stop. Do NOT continue.**\n\nIf both exist, proceed to STEP 3 silently.\n\n---\n\n## STEP 3: Greet and Ask for 3 Values\n\nSend this message:\n\n> (CN) 好，我来帮你接入 OpenViking 长期记忆。装好之后，我会自动记住对话里的重要信息，下次聊也能回忆起来。\n>\n> 我需要 3 条信息，不知道的可以问你的管理员：\n> 1. **OpenViking 服务地址** —— 例如 `https://ov.example.com` 或 `http://192.168.1.100:1933`，本机服务可以直接说\"本机\"\n> 2. **API Key** —— 用来鉴权；服务没开认证可以说\"没有\"\n> 3. **记忆归属**（可选） —— 所有对话共用 user 记忆（默认），还是在 `peers/<assistant_id>` 或 `peers/<sender_id>` 下分开存放\n>\n> 先告诉我服务地址吧？\n\n> (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.\n>\n> I need 3 things (ask your admin if unsure):\n> 1. **OpenViking server URL** — e.g. `https://ov.example.com` or `http://192.168.1.100:1933`. For a local server, just say \"local\".\n> 2. **API Key** — for auth. Say \"none\" if the server has no auth.\n> 3. **Memory scope** (optional) — shared user memory (default), or peer memory under `peers/<assistant_id>` / `peers/<sender_id>`.\n>\n> What's the server URL?\n\n---\n\n## STEP 4: Collect Values\n\nCollect 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.\n\n### 4a. `BASE_URL` (REQUIRED)\n\n- \"local\" / \"本机\" / \"localhost\" → use `http://127.0.0.1:1933`.\n- `ov.example.com` without protocol → prepend `https://`.\n- Strip trailing `/`, `/health`, or `/api`.\n- After normalization must start with `http://` or `https://`.\n- 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.**\n\n### 4b. `API_KEY` (OPTIONAL)\n\n> (CN) API Key 是什么？服务没开认证就直接说\"没有\"。\n> (EN) What's the API Key? Say \"none\" if the server has no auth.\n\n- \"no\" / \"none\" / \"没有\" / \"空\" / empty → `API_KEY=\"\"` (you will skip the flag later).\n- Otherwise store as-is.\n\n### 4c. `PEER_ROLE` (OPTIONAL)\n\n> (CN) 记忆要存在哪一层？默认共享在 `viking://user/<user_id>/memories`（`none`）。也可以按 OpenClaw 助手存到 `.../peers/<assistant_id>/memories`（`assistant`），或按发送者存到 `.../peers/<sender_id>/memories`（`sender`）。\n> (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`).\n\nDefault to `none`. **Never silently pick another value** — the three options mean different storage layouts and are awkward to change later:\n\n| Value | Concrete layout | Choose it when |\n|---|---|---|\n| `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. |\n| `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/...`. |\n| `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. |\n\n`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`.\n\nOpenViking 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.\n\n- Empty / \"default\" / \"默认\" / \"共用\" → leave unset (plugin defaults to `none`).\n- Otherwise pass `--peer-role none|assistant|sender`. Treat explicit legacy `person` as `sender`. Any other value → ask again.\n\n### 4d. `PEER_PREFIX` (OPTIONAL, only when `PEER_ROLE=assistant`)\n\n> (CN) 想给这个 agent 一个记忆前缀吗？留空就用默认。只能用字母、数字、`_`、`-`。\n> (EN) Want to set an peer prefix? Leave blank for the default. Letters, digits, `_`, `-` only.\n\n- Only meaningful when `PEER_ROLE=assistant`; skip the question otherwise.\n- Empty / \"default\" / \"默认\" → leave unset (plugin defaults to `\"\"`).\n- Otherwise validate against `/^[A-Za-z0-9_-]+$/`. If invalid, ask again.\n\n### 4e. (Conditional) Multi-Tenant Root-Key Fields\n\nOnly ask for these if STEP 7 detects a root key (`Root API key detected. Missing: --account-id, --user-id`). Don't ask up front.\n\n- `ACCOUNT_ID`\n- `USER_ID`\n\nSee **Reference: Multi-Tenant** for what these mean.\n\n---\n\n## STEP 5: Pre-flight Connectivity Check (silently)\n\nTell the user briefly:\n\n> (CN) 我先测一下能不能连上服务……\n> (EN) Let me test the connection to your server...\n\nRun:\n\n**If OS=windows:**\n\n```powershell\ntry { (Invoke-WebRequest -Uri \"BASE_URL/health\" -TimeoutSec 10 -UseBasicParsing -ErrorAction Stop).StatusCode } catch { $_.Exception.Response.StatusCode.value__ }\n```\n\n**If OS=mac or OS=linux:**\n\n```bash\ncurl -sS -o /dev/null -w \"%{http_code}\" --connect-timeout 10 \"BASE_URL/health\"\n```\n\nReplace `BASE_URL` with the actual value.\n\n| Status | Meaning | Action |\n|---|---|---|\n| `200` | Server reachable, no auth on `/health` | Proceed to STEP 6. |\n| `401` / `403` | Server reachable but `/health` requires auth | Proceed to STEP 6 — the wizard's key probe will sort it out. |\n| `000` / timeout / connection refused | Server unreachable | **Server-not-ready handling** below. |\n| Anything else | Unexpected | Show status code to the user, go back to STEP 4a. |\n\n### Server-not-ready handling\n\nThis skill **does not install or operate the OpenViking server**. If the user's server is unreachable, present the situation honestly and offer two paths:\n\n> (CN) ❌ 我连不上 `BASE_URL`。可能是：\n> 1) 服务还没启动 —— 请联系你的 OpenViking 服务管理员把它起起来；如果是你自己负责，请参考 OpenViking 官方文档（`https://github.com/volcengine/OpenViking`）的 server 启动指引。\n> 2) 地址不对 —— 你可以重新告诉我正确的地址。\n> 3) 网络不通（防火墙 / VPN / 内网）—— 你确认一下网络。\n>\n> 也可以选择\"先把配置写下来\"，等服务起来就自动生效，要这么办吗？\n\n> (EN) ❌ Cannot reach `BASE_URL`. Likely cause:\n> 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.**\n> 2) **Wrong URL** — give me the correct URL.\n> 3) **Network blocked** (firewall / VPN / private network) — please verify connectivity.\n>\n> Or I can save the config now (`--allow-offline`) so it will activate automatically once the server is up. Want me to do that?\n\nIf the user fixes the URL → back to STEP 4a.\nIf the user wants `--allow-offline` → remember `ALLOW_OFFLINE=true` and continue to STEP 6.\nIf the user gives up / cannot fix → stop here. Do NOT continue with a broken state.\n\n---\n\n## STEP 6: Install the Plugin\n\nThe 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.\n\n### Path A — Primary: `openclaw plugins install` (uses ClawHub)\n\nTell the user:\n\n> (CN) 现在开始装插件……\n> (EN) Installing the plugin now...\n\nRun:\n\n```bash\nopenclaw plugins install clawhub:@openviking/openclaw-plugin\n```\n\nTrigger fallback to Path B only if the output contains any of these strings:\n\n- `429`\n- `rate limit` / `rate-limited` / `Too Many Requests`\n- `not logged in` / `please log in` / `please login` / `unauthorized` / `401` / `403` together with `clawhub`\n- `ETIMEDOUT` / `ECONNRESET` on a `clawhub`-related host\n- generic message indicating the registry refused an anonymous client\n\nBefore falling back, also try the explicit registry prefix once:\n\n```bash\nopenclaw plugins install clawhub:@openviking/openclaw-plugin\n```\n\nIf the install **succeeds**, jump to STEP 7.\n\nIf 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.\n\n### Path B — Backup: `ov-install` (bypasses ClawHub)\n\nTell the user:\n\n> (CN) ClawHub 现在好像被限流、不可用，或者当前账号不能安装。我改用备用路径，通过 npm 下载并部署插件包。\n> (EN) ClawHub looks rate-limited, unavailable, or blocked for this account. I'll use the backup path and install the plugin package from npm.\n\nRun the installer with `npx` (no global install needed):\n\n```bash\nnpx -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]\n```\n\nBuild the flag list according to what the user gave you:\n\n- Always pass `--base-url BASE_URL`.\n- Pass `--api-key API_KEY` only if `API_KEY` is non-empty.\n- 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.\n- Pass `--peer-prefix PEER_PREFIX` only if the user gave one and `PEER_ROLE=assistant`.\n- `--account-id` / `--user-id` only if the root-key path requires them.\n\n`ov-install` will, in one shot:\n1. Download the `@openviking/openclaw-plugin` package from npm into a temporary staging dir.\n2. Copy the package into the OpenClaw `extensions/` dir and install plugin dependencies.\n3. Register the plugin in `openclaw.json` (via `openclaw plugins enable` or direct write).\n4. Run `openclaw openviking setup --json --base-url … [--api-key …]` for the user.\n5. Return a non-zero exit if setup needs explicit `--allow-offline` or `--force-slot` consent.\n\nThis 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).\n\nIf `ov-install` exits non-zero, capture the last 30 lines of its output, show them to the user, and stop. Don't retry blindly.\n\n---\n\n## STEP 7: Configure (only on Path A — Path B did this for you)\n\nRun the setup wizard non-interactively. Build flags from collected values:\n\n```bash\nopenclaw 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]\n```\n\nRules:\n\n- `--base-url BASE_URL` is **required** under `--json`. Without it, the wizard prints `--json requires --base-url for non-interactive mode`.\n- `--api-key` only if `API_KEY` is non-empty.\n- `--peer-role` only if the user chose `assistant` or `sender`; omit it for the `none` default. Normalize legacy `person` to `sender` first.\n- `--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.\n- `--account-id` / `--user-id` only after STEP 7 root-key detection (see below).\n- `--allow-offline` only if the user explicitly approved it in STEP 5.\n- `--force-slot` **never** in the first attempt. Add only after the user confirms (see slot_blocked handling below).\n\n### Parse the JSON output\n\nThe wizard prints a single JSON object:\n\n```json\n{\n  \"success\": true | false,\n  \"action\": \"configured\" | \"existing\" | \"error\" | \"slot_blocked\",\n  \"config\": { \"mode\": \"remote\", \"baseUrl\": \"...\", \"apiKey\": \"...\", \"peer_prefix\": \"...\", \"accountId\": \"...\", \"userId\": \"...\" },\n  \"health\": { \"ok\": true, \"version\": \"...\", \"compatibility\": \"compatible\" | \"server_too_old\" | \"server_too_new\" | \"unknown\" },\n  \"keyProbe\": { \"keyType\": \"user_key\" | \"root_key\" | \"no_key\" | \"unknown\", \"needsAccountId\": false, \"needsUserId\": false, \"detail\": \"...\" },\n  \"slot\": { \"activated\": true, \"replaced\": false, \"previousOwner\": \"openviking\" },\n  \"error\": \"...\"\n}\n```\n\n### Decision matrix\n\n| Condition | Action |\n|---|---|\n| `success: true` and `action: \"configured\"` or `\"existing\"` | Done. Proceed to STEP 9. |\n| `success: false` and `action: \"slot_blocked\"` | **Slot conflict — see below.** |\n| `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. |\n| `success: false` and `error` contains `\"Root API key detected\"` and `\"Missing: --account-id, --user-id\"` | **Root-key path — see below.** |\n| `success: false` and `error` contains `\"Invalid API key\"` / `keyProbe.keyType: \"unknown\"` after auth failure detail | API key wrong. Back to STEP 4b. |\n| `success: false` and any other `action: \"error\"` | Show `error` to the user, stop. Do NOT pretend success. |\n\n### Slot conflict (`slot_blocked`)\n\nThe error text looks like:\n\n> `contextEngine slot is owned by \"<other-plugin>\". Config was saved but slot was NOT changed. Use --force-slot to replace.`\n\n**Do NOT silently use `--force-slot`.** Ask the user:\n\n> (CN) 你的 OpenClaw 当前 contextEngine 槽被 `<other-plugin>` 占着。如果用 OpenViking 替换它，`<other-plugin>` 就不再生效。要替换吗？\n> (EN) Your `contextEngine` slot is currently owned by `<other-plugin>`. Activating OpenViking will disable it. Replace?\n\nIf 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.\n\n### Root-key path\n\nThe error text looks like:\n\n> `Root API key detected. Missing: --account-id, --user-id. Provide both to enable multi-tenant scoping.`\n\nAsk the user:\n\n> (CN) 你给的是 root 级 API Key，需要再补两个值才能用：账户 ID（accountId）和用户 ID（userId）。这两个一般是 OpenViking 服务管理员配的，不知道就问他们。\n> (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.\n\nAfter collecting, retry the setup command with `--account-id` and `--user-id` appended.\n\n---\n\n## STEP 8: (Reserved — done inside STEP 7 or by `ov-install`)\n\nThe 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.\n\n---\n\n## STEP 9: Restart the Gateway\n\n```bash\nopenclaw gateway restart\n```\n\nIf it fails, try once more with:\n\n```bash\nopenclaw gateway --force\n```\n\nIf both fail:\n\n> (CN) Gateway 没能自动重启。请你手动跑一下 `openclaw gateway restart`。重启完告诉我，我来验证。\n> (EN) Gateway didn't restart cleanly. Please run `openclaw gateway restart` manually, then tell me when it's done so I can verify.\n\nWait ~3 seconds before STEP 10.\n\n---\n\n## STEP 10: Verify\n\n```bash\nopenclaw openviking status --json\n```\n\nExpected output:\n\n```json\n{\n  \"configured\": true,\n  \"slotActive\": true,\n  \"health\": { \"ok\": true },\n  \"config\": { \"baseUrl\": \"...\", \"peer_prefix\": \"...\" }\n}\n```\n\n### Decision\n\n- `configured: true` **and** `slotActive: true` **and** `health.ok: true` → **success.** Send the success message below.\n- `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.\n- `configured: true` but `health.ok: false` → server became unreachable. Tell the user; the config will start working as soon as the server is back.\n- `configured: false` → setup didn't actually persist. Show full status JSON to the user and stop.\n\n### Success message\n\n> (CN) 🎉 全部搞定！OpenViking 长期记忆已经接好了。\n>\n> 工作方式：\n> - **每一轮**：你的对话会被自动追加到 OpenViking session 里；自动抽取依赖阈值 commit 或 `/compact`\n> - **明确要记住时**：如果你说“记住/保存/存一下”某个长期事实，我会用 `memory_store` 立即提交到记忆管线\n> - **`/compact` 时**：待提交的 session 消息会在 OpenViking 服务端被抽取成长期记忆\n> - **后续会话**：每次回复前我都会自动搜一下相关记忆并带进上下文\n>\n> 想验证一下吗？可以直接说“记住我的邮箱是 test@example.com”，让我通过 `memory_store` 立即提交；或者先聊几句包含可记忆事实的话，再调一下 `/compact`。之后开新对话问“我的邮箱是？”，应该就能召回了。\n\n> (EN) 🎉 All set! OpenViking long-term memory is connected.\n>\n> How it works:\n> - **Every turn**: our messages are appended to an OpenViking session; automatic extraction depends on a threshold commit or `/compact`\n> - **Explicit remember requests**: if you ask me to remember/save/store a long-term fact, I can use `memory_store` to commit it immediately\n> - **On `/compact`**: pending session messages are extracted into long-term memories on the OpenViking server\n> - **Future sessions**: relevant memories are auto-retrieved and injected before my replies\n>\n> 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.\n\n---\n\n# Part 2 — Reference\n\n## How It Works\n\nThe 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:\n\n- **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/`.\n- **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.\n  - `captureMode: \"semantic\"` (default): server extraction pipeline filters all qualifying text.\n  - `captureMode: \"keyword\"`: only text matching trigger words (e.g. \"remember\", \"preference\") is considered.\n- **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.\n\n**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.\n\n### Explicit long-term memory writes\n\nAuto-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.\n\nUse `memory_store` as the integration-side reliable path for durable-memory intent:\n\n- It writes the supplied text into an OpenViking session and calls `commit(wait=true)`.\n- It complements auto-capture; it does not replace normal session capture.\n- 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.\n\n## Available Tools\n\nThese are the plugin tools the agent can call once installed.\n\n### `memory_recall` — Search Memories\n\n| Parameter | Required | Description |\n|---|---|---|\n| `query` | Yes | Search query text |\n| `limit` | No | Maximum number of results (defaults to plugin config) |\n| `scoreThreshold` | No | Minimum relevance score 0–1 (defaults to plugin config) |\n| `targetUri` | No | Exact search scope URI. If provided, only that URI is searched. |\n| `resourceTypes` | No | Array of target types used when `targetUri` is omitted: `resource`, `user`, `agent`. Defaults to plugin `recallTargetTypes`. |\n\nExample: user asks \"What programming language did I say I like?\"\n\n### `memory_store` — Manual Store\n\n| Parameter | Required | Description |\n|---|---|---|\n| `text` | Yes | Information text to store |\n| `role` | No | Session role (default `user`) |\n| `sessionId` | No | Existing OpenViking session ID |\n\nUse this when the user explicitly asks to remember/save/store a long-term fact, preference, project, or decision.\n\nExample: user says \"Remember my email is xxx@example.com\".\n\n### `memory_forget` — Delete Memories\n\n| Parameter | Required | Description |\n|---|---|---|\n| `uri` | No | Exact memory URI (direct delete) |\n| `query` | No | Search query (find then delete) |\n| `targetUri` | No | Search scope URI |\n| `limit` | No | Search limit (default 5) |\n| `scoreThreshold` | No | Minimum relevance score |\n\nExample: user says \"Forget my phone number\".\n\n### `ov_archive_search` — Search Archived Original Messages\n\n| Parameter | Required | Description |\n|---|---|---|\n| `query` | Yes | A single keyword or short phrase. Prefer concrete names, dates, commands, paths, or distinctive nouns. |\n| `archiveId` | No | Optional archive ID such as `archive_002`. |\n\nUse when `[Session History Summary]` is too coarse. Try at least two keyword variants before concluding the detail is unavailable.\n\n### `ov_archive_expand` — Expand An Archive\n\n| Parameter | Required | Description |\n|---|---|---|\n| `archiveId` | Yes | Archive ID from `[Archive Index]`, e.g. `archive_002`. |\n\n### `add_resource` — Import Resource\n\nThe 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.\n\n| Parameter | Required | Description |\n|---|---|---|\n| `source` | Yes | Local path, OpenClaw media attachment path, directory path, public URL, or Git URL. |\n| `to` | No | Exact target URI under `viking://resources`; mutually exclusive with `parent`. |\n| `parent` | No | Parent URI under `viking://resources`; mutually exclusive with `to`. |\n| `reason` | No | Reason or note for import. |\n| `instruction` | No | Processing instruction for semantic extraction. |\n| `wait` | No | Wait for processing completion. |\n\n### `add_skill` — Import Agent Skill\n\n| Parameter | Required | Description |\n|---|---|---|\n| `source` | No | Local `SKILL.md` path or skill directory. Exactly one of `source` or `data` is required. |\n| `data` | No | Raw `SKILL.md` content or MCP tool dict. Exactly one of `source` or `data` is required. |\n| `wait` | No | Wait for processing completion. |\n\nAgent 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.\n\n### `ov_search` — Search Resources and Skills\n\n| Parameter | Required | Description |\n|---|---|---|\n| `query` | Yes | Search query. |\n| `uri` | No | Optional search URI. Defaults to resources plus agent skills. |\n| `limit` | No | Max results per search scope. Default: 10. |\n\nImportant: `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.\n\n### `ov_read` — Read OpenViking Content\n\n| Parameter | Required | Description |\n|---|---|---|\n| `uri` | Yes | Exact `viking://...` URI returned by `ov_search` or recall trace results. Local file paths and `openviking://...` display aliases are refused. |\n\n### `ov_recall_trace` — Query Recall Trace\n\n| Parameter | Required | Description |\n|---|---|---|\n| `turn` | No | `latest` or `all`; default `latest`. |\n| `traceId` | No | Exact trace ID. |\n| `sessionId` / `sessionKey` / `ovSessionId` | No | Session filters. |\n| `source` | No | `auto_recall`, `memory_recall`, `ov_search`, or `ov_archive_search`. |\n| `resourceTypes` | No | Target type filters: `resource`, `user`, `agent`. |\n| `since` / `until` | No | Unix timestamp bounds in milliseconds. |\n| `includeContent` | No | Read selected/displayed URI content previews on demand. |\n| `limit` | No | Maximum traces to return. Default: 20. |\n\nRequires `traceRecall=true`; persisted lookup also requires `traceRecallPersist=true`.\n\n### Externalized Tool Result Tools\n\nUse these when a preview contains a `viking://session/<session_id>/tool-results/<tool_result_id>` ref.\n\n| Tool | Parameters |\n|---|---|\n| `openviking_tool_result_list` | `tool_name?`, `limit?` (default 50) |\n| `openviking_tool_result_search` | `tool_output_ref`, `query`, `limit?` (default 20), `context_chars?` (default 300) |\n| `openviking_tool_result_read` | `tool_output_ref`, `offset?` (default 0), `limit?` (default 20000) |\n\n## Configuration Schema\n\nThese are the keys under `plugins.entries.openviking.config` in `openclaw.json`. The setup wizard / `ov-install` sets the first few; the rest are tunables.\n\n| Field | Default | Description |\n|---|---|---|\n| `mode` | `\"remote\"` (forced by plugin) | Always remote in this skill. Don't set manually. |\n| `baseUrl` | `http://127.0.0.1:1933` | OpenViking server URL. |\n| `apiKey` | — | API key. Optional if server has no auth. |\n| `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`. |\n| `peer_prefix` | `\"\"` | Optional prefix for assistant `peer_id` / actor peer values. Only used when `peer_role=assistant`. Letters / digits / `_` / `-`. |\n| `accountId` | — | Required when `apiKey` is a root key. |\n| `userId` | — | Required when `apiKey` is a root key. |\n| `targetUri` | `viking://~/memories` | Default search scope URI. |\n| `timeoutMs` | (plugin default) | HTTP timeout for OpenViking calls. |\n| `autoCapture` | `true` | Auto-append turn messages to the OpenViking session at `afterTurn`; extraction runs only after a threshold commit, `/compact`, or explicit `memory_store`. |\n| `captureMode` | `\"semantic\"` | Filter mode used by the server-side extraction pipeline: `semantic` or `keyword`. |\n| `captureMaxLength` | `24000` | Max text length per archived turn. |\n| `autoRecall` | `true` | Auto-recall and inject memories before reply. |\n| `recallTargetTypes` | `user,agent` | Default target types when `targetUri` is omitted. Allowed: `resource`, `user`, `agent`. |\n| `recallResources` | `false` | Compatibility shortcut that appends `resource` to default recall targets when `recallTargetTypes` is unset. |\n| `recallLimit` | `6` | Max memories injected per recall. |\n| `recallScoreThreshold` | `0.15` | Min relevance score to inject. |\n| `recallMaxInjectedChars` | `4000` | Hard cap on injected character count. Complete memories that do not fit are skipped, not truncated. |\n| `recallPreferAbstract` | (plugin default) | Prefer abstract memories over raw. |\n| `recallTokenBudget` | deprecated | Compatibility alias for `recallMaxInjectedChars`. |\n| `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. |\n| `commitKeepRecentCount` | `10` | Recent messages kept live after afterTurn commit. Compact always uses `0`. |\n| `bypassSessionPatterns` | — | Glob patterns for sessions skipped by capture. |\n| `ingestReplyAssist` | (plugin default) | Reply-assist ingestion toggle. |\n| `emitStandardDiagnostics` | `false` | Emit structured `openviking: diag {...}` lines. |\n| `logFindRequests` | `false` | Log routing for find/session writes. Also enabled by `OPENVIKING_LOG_ROUTING=1` or `OPENVIKING_DEBUG=1`. |\n| `traceRecall` | `false` | Record recall traces in memory. |\n| `traceRecallPersist` | `false` | Persist recall traces to JSONL files. |\n| `traceRecallDir` | `~/.openclaw/openviking/recall-traces` | Recall trace directory. |\n\nTo change a value:\n\n```bash\nopenclaw config set plugins.entries.openviking.config.<field> <value>\nopenclaw gateway restart\n```\n\n## Multi-Tenant (Root API Keys)\n\nSome 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:\n\n```\nRoot API key detected. Missing: --account-id, --user-id\n```\n\nWhen you see this:\n\n1. Ask the user for both values (they come from the OpenViking admin).\n2. Retry STEP 7 with both flags:\n\n```bash\nopenclaw openviking setup --base-url BASE_URL --api-key API_KEY --account-id ACCOUNT_ID --user-id USER_ID --json\n```\n\nA **user key** (issued per tenant) does not need these flags.\n\n## Multi-Instance (`--workdir` / `OPENCLAW_STATE_DIR`)\n\nIf the user runs multiple OpenClaw instances (e.g. testing several agents in parallel), each has its own state dir.\n\nTo target a non-default instance:\n\n```bash\nnpx -y openclaw-openviking-setup-helper@latest --workdir ~/.openclaw-second --base-url ... --api-key ...\n```\n\n`ov-install` writes a helper env file when the state dir is non-default:\n\n- Unix: `~/.openclaw/openviking.env` containing `export OPENCLAW_STATE_DIR='...'`\n- Windows: `~/.openclaw/openviking.env.bat` and `.ps1` setting the same variable\n\nSource it before running `openclaw` commands so they hit the correct state:\n\n**Unix:**\n```bash\nsource ~/.openclaw/openviking.env\nopenclaw status\n```\n\n**Windows (PowerShell):**\n```powershell\n. \"$HOME/.openclaw/openviking.env.ps1\"\nopenclaw status\n```\n\nOr pass `--workdir` directly to each `openclaw` invocation (note: not all `openclaw` subcommands honor `--workdir` consistently — when in doubt, prefer the env var).\n\n## Daily Operations\n\n```bash\n# Start or restart OpenClaw gateway after config changes\nopenclaw gateway restart\n\n# Check overall status\nopenclaw status\nopenclaw openviking status --json\n\n# Read current OpenViking slot\nopenclaw config get plugins.slots.contextEngine\n\n# Disable OpenViking memory (keep config, deactivate slot)\nopenclaw config set plugins.slots.contextEngine legacy\nopenclaw gateway restart\n\n# Re-enable\nopenclaw config set plugins.slots.contextEngine openviking\nopenclaw gateway restart\n```\n\n## Uninstall\n\n### Preferred: via OpenClaw plugin manager\n\n```bash\nopenclaw plugins uninstall openviking\nopenclaw config set plugins.slots.contextEngine legacy\nopenclaw gateway restart\n```\n\n### If installed through backup `ov-install`\n\n```bash\nnpx -y openclaw-openviking-setup-helper@latest --uninstall\n```\n\nThis will:\n\n1. Stop the OpenClaw gateway.\n2. Back up `openclaw.json`.\n3. Remove all OpenViking plugin config from `openclaw.json` (`plugins.entries.openviking`, `plugins.allow`, `plugins.installs.openviking`, `plugins.slots.contextEngine`).\n4. Move the plugin directory to `disabled-extensions/` as backup.\n5. Remove the helper env files (`openviking.env`, `.bat`, `.ps1`).\n\nAdd `--base-url dummy` for fully non-interactive mode (skips the confirmation prompt).\n\n### Manual\n\n```bash\nopenclaw gateway stop\nopenclaw config set plugins.slots.contextEngine legacy\n# Then edit ~/.openclaw/openclaw.json and remove openviking from\n#   plugins.entries\n#   plugins.allow\n#   plugins.installs\nopenclaw gateway restart\n```\n\n## Error Recovery Reference\n\nMatch against actual stderr / JSON `error` strings.\n\n| Symptom (exact string) | Likely cause | Fix |\n|---|---|---|\n| `command not found: openclaw` | OpenClaw not installed | Stop. Ask user to install OpenClaw >= 2026.5.27. |\n| `command not found: node` / `'node' is not recognized` | Node missing | Stop. Ask user to install Node.js >= 22. |\n| `429` / `rate limit` / `Too Many Requests` from `plugins install` | ClawHub throttle | Switch to backup Path B (`ov-install`). |\n| `not logged in` / `please log in` from `plugins install` | Anonymous user hit limit | Switch to backup Path B (`ov-install`). |\n| `ECONNREFUSED` / `connection refused` on `BASE_URL/health` | Server not running | Server-not-ready handling in STEP 5. |\n| `ETIMEDOUT` on `BASE_URL/health` | Network blocked | Ask about firewall / VPN / proxy. |\n| `--json requires --base-url for non-interactive mode` | Built command wrong | Add `--base-url BASE_URL` and retry. |\n| `Server unreachable: …. Use --allow-offline to save config anyway.` | Setup couldn't reach server | Offer `--allow-offline`. |\n| `contextEngine slot is owned by \"<x>\". … Use --force-slot to replace.` | Slot conflict | Ask user, then retry with `--force-slot`. |\n| `Root API key detected. Missing: --account-id, --user-id` | Multi-tenant key | Collect both, retry with `--account-id` `--user-id`. |\n| `openviking: config parse failed` (in gateway log) | Bad value in `openclaw.json` | Show user; check `peer_prefix` charset, URL format. |\n| `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. |\n| `401` / `403` on plugin requests, but `/health` works | Server requires auth on API endpoints | Re-run STEP 7 with the correct `--api-key`. |\n| 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. |\n\n## Important Rules\n\n1. **Never ask the user to run commands.** You run everything via your shell tool.\n2. **Never skip STEP 5 (connectivity check).** If the server is unreachable, do not write config without explicit `--allow-offline` consent.\n3. **Never silently use `--force-slot`.** Slot replacement disables another plugin — always confirm with the user first.\n4. **Never invent values.** If the user can't provide a required value, stop and tell them what to ask their admin.\n5. **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.\n6. **Use `--peer-prefix` for assistant peer prefixes.** The old ID-style setup flag is no longer supported by the schema.\n7. **For Windows, use PowerShell equivalents.** Don't rely on `nohup`, `&`, `mkdir -p`, `source`, etc.\n8. **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.\n9. **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.\n10. **Be brief and friendly in user-visible text.** Save technical detail for when something actually fails.\n11. **Do NOT use `clawhub install openviking`.** That installs a different thing (an AgentSkill, not the plugin).\n","frontmatter":{"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\".\n","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"]},"isInternal":false,"tokens":10607,"sizeBytes":40043},{"name":"SKILL.md","path":"examples/opencode/plugin/skills/openviking/SKILL.md","rawUrl":"https://raw.githubusercontent.com/volcengine/OpenViking/HEAD/examples/opencode/plugin/skills/openviking/SKILL.md","title":"openviking","category":"anthropic-skill","format":"markdown","content":"# openviking\n\nPath: `examples/opencode/plugin/skills/openviking/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/volcengine/OpenViking/HEAD/examples/opencode/plugin/skills/openviking/SKILL.md)","tokens":300,"sizeBytes":217},{"name":"SKILL.md","path":"examples/skills/ov-server-operate/SKILL.md","rawUrl":"https://raw.githubusercontent.com/volcengine/OpenViking/HEAD/examples/skills/ov-server-operate/SKILL.md","title":"ov-server-operate","category":"anthropic-skill","format":"markdown","content":"---\nname: ov-server-operate\ndescription: Operate and maintain OpenViking server - configure, install, start, stop, and cleanup the server. Use when need to setup or manage OpenViking service deployment.\ncompatibility: OpenViking CLI configured at `~/.openviking/ovcli.conf`\n---\n\n# OpenViking Server Operations\n\nThis guide provides standard operating procedures for deploying, managing, and maintaining OpenViking servers in production environments.\n\n## Table of Content\n- Service Configuration\n- Environment Setup with uv\n- Server Startup with nohup\n- Server Shutdown\n- Data Cleanup Procedure\n- Verification and Troubleshooting\n\n## Service Configuration\n\n### Default Paths and Structure\n\nOpenViking uses the following standard directory structure under `~/.openviking/`:\n\n```\n~/.openviking/\n├── ov.conf             # Server configuration (required)\n├── ovcli.conf          # CLI client configuration\n├── ov-venv/            # Virtual environment (created by uv)\n├── log/                # Server log directory\n│   ├── openviking-server.log   # server stdout log\n│   └── openviking.log          # server log\n└── data/               # Workspace data (configured in ov.conf)\n    ├── ...\n    └── ...\n```\n\n### Configuration Files\n\n#### 1. Server Config (`~/.openviking/ov.conf`)\n\nCreate the configuration file with at minimum the following configuration.\nNote 1: Replace the api-key with your own api-key. If you don't have one, ask the user to get one (follow the Volcengine Ark platform guide).\nNote 2: Replace the root_api_key with your own root-api-key. Ask the user to set one — it will be used for authentication when the CLI connects to the server.\n\n```json\n{\n  \"server\": {\n    \"host\": \"0.0.0.0\",\n    \"port\": 1933,\n    \"root_api_key\": \"your-root-api-key\"\n  },\n  \"storage\": {\n    \"workspace\": \"~/.openviking/data/\"\n  },\n  \"parsers\": {\n    \"code\": {\n      \"gitlab_domains\": [\"code.byted.org\"],\n      \"azure_devops_domains\": [\"ssh.dev.azure.com\", \"vs-ssh.visualstudio.com\"]\n    }\n  },\n  \"embedding\": {\n    \"dense\": {\n        \"model\": \"doubao-embedding-vision-251215\",\n        \"api_key\": \"your-volcengine-api-key\",\n        \"api_base\": \"https://ark.cn-beijing.volces.com/api/v3\",\n        \"dimension\": 1024,\n        \"input\": \"multimodal\",\n        \"provider\": \"volcengine\"\n    }\n  },\n  \"vlm\": {\n    \"model\": \"doubao-seed-1-8-251228\",\n    \"api_key\": \"your-volcengine-api-key\",\n    \"api_base\": \"https://ark.cn-beijing.volces.com/api/v3\",\n    \"temperature\": 0.0,\n    \"max_retries\": 2,\n    \"provider\": \"volcengine\",\n    \"thinking\": false\n  },\n  \"log\": {\n    \"level\": \"INFO\",\n    \"output\": \"file\",\n    \"rotation\": true,\n    \"rotation_days\": 3,\n    \"rotation_interval\": \"midnight\"\n  }\n}\n```\n\n#### 2. CLI Config (`~/.openviking/ovcli.conf`)\n\nFor client connections from localhost:\n\n```json\n{\n  \"url\": \"http://localhost:1933\",\n  \"api_key\": \"your-root-api-key\"\n}\n```\n\nFor remote connections, set the url to the remote server address (for example, the server EIP).\n\n## Environment Setup with uv\n\n### Step 1: Install uv (if not already installed)\n\n```bash\n# macOS/Linux\ncurl -LsSf https://astral.sh/uv/install.sh | sh\n\n# Verify installation\nuv --version\n```\n\n### Step 2: Create Virtual Environment\n\nCreate a dedicated virtual environment at `~/.openviking/ov-venv`:\n\n```bash\n# Create venv with Python 3.10+\ncd ~/.openviking\nuv venv --python 3.12 ov-venv\n```\n\n### Step 3: Activate and Install OpenViking\n\n```bash\n# Activate the virtual environment\nsource ~/.openviking/ov-venv/bin/activate\n\n# Install or upgrade to latest openviking\nuv pip install --upgrade openviking --force-reinstall\n\n# Verify installation\nwhich openviking-server\nopenviking-server --version\nopenviking-server --help\n```\n\n### Step 4: Create Log Directory\n\n```bash\nmkdir -p ~/.openviking/log\n```\n\n## Server Startup with nohup\n\n### Standard Startup Procedure\n\n```bash\n# 1. Activate the virtual environment\nsource ~/.openviking/ov-venv/bin/activate\n\n# 2. Ensure log directory exists\nmkdir -p ~/.openviking/log\n\n# 3. Start server with nohup\nnohup openviking-server \\\n    > ~/.openviking/log/openviking-server.log 2>&1 &\n\n# 4. Save PID for later reference\necho $! > ~/.openviking/server.pid\n\n# 5. Verify startup after 10 secs\nsleep 10\ncurl -s http://localhost:1933/health\n```\n\n### Verify Server is Running\n\n```bash\n# Method 1: Check health endpoint\ncurl http://localhost:1933/health\n# Expected: {\"status\": \"ok\"}\n\n# Method 2: Check readiness (includes storage checks)\ncurl http://localhost:1933/ready\n\n# Method 3: Check process\nps aux | grep openviking-server | grep -v grep\n\n# Method 4: Check log output\ntail -10 ~/.openviking/log/openviking-server.log\ntail -50 ~/.openviking/log/openviking.log\n```\n\n## Server Shutdown\n\n### Graceful Shutdown Procedure\n\n```bash\n# 1. Find the server process\nps aux | grep openviking-server | grep -v grep\n\n# 2. Send SIGTERM for graceful shutdown\n# Option A: Using saved PID\nif [ -f ~/.openviking/server.pid ]; then\n    kill $(cat ~/.openviking/server.pid)\n    rm ~/.openviking/server.pid\nfi\n\n# Option B: Using pgrep\npkill -f openviking-server\n\n# 3. Wait for process to stop\nsleep 3\n\n# 4. Verify it stopped\nps aux | grep openviking-server | grep -v grep || echo \"Server stopped successfully\"\n\n# 5. If still running, force kill\nif pgrep -f openviking-server > /dev/null; then\n    echo \"Force killing server...\"\n    pkill -9 -f openviking-server\nfi\n```\n\n## Data Cleanup Procedure\n\n### When to Use This Procedure\n\nPerform full data cleanup in these scenarios:\n1. Version upgrade with incompatible data format\n2. Corrupted or inconsistent data\n3. Need to reset to fresh state\n4. Storage space reclamation\n\n### Standard Cleanup Workflow\n\n**CRITICAL: ALWAYS BACKUP BEFORE DELETING DATA**\n\n```bash\n# ==========================================\n# STEP 1: STOP THE SERVER FIRST\n# ==========================================\necho \"Step 1: Stopping OpenViking Server...\"\nif pgrep -f openviking-server > /dev/null; then\n    pkill -f openviking-server\n    sleep 3\n    if pgrep -f openviking-server > /dev/null; then\n        pkill -9 -f openviking-server\n        sleep 1\n    fi\nfi\n\n# Verify server is stopped\nif pgrep -f openviking-server > /dev/null; then\n    echo \"ERROR: Server still running! Cannot proceed.\"\n    exit 1\nfi\necho \"✓ Server stopped\"\n\n# ==========================================\n# STEP 2: CREATE BACKUP (REQUIRED)\n# ==========================================\necho \"\"\necho \"Step 2: Creating backup...\"\nBACKUP_DATE=$(date +%Y%m%d_%H%M%S)\nBACKUP_DIR=~/.openviking/backup_${BACKUP_DATE}\n\nmkdir -p ${BACKUP_DIR}\n\n# Backup config files\ncp ~/.openviking/ov.conf ${BACKUP_DIR}/ 2>/dev/null || true\ncp ~/.openviking/ovcli.conf ${BACKUP_DIR}/ 2>/dev/null || true\n\n# Backup workspace (if exists)\nWORKSPACE=$(python3 -c '\nimport json\nimport os\nconfig_path = os.path.expanduser(\"~/.openviking/ov.conf\")\nif os.path.exists(config_path):\n    with open(config_path) as f:\n        cfg = json.load(f)\n        ws = cfg.get(\"storage\", {}).get(\"workspace\", \"./data\")\n        print(os.path.expanduser(ws))\n' 2>/dev/null || echo \"~/.openviking/data\")\n\nif [ -d \"${WORKSPACE}\" ]; then\n    echo \"Backing up workspace: ${WORKSPACE}\"\n    tar -czf ${BACKUP_DIR}/workspace_backup.tar.gz -C $(dirname ${WORKSPACE}) $(basename ${WORKSPACE})\nfi\n\n# Backup log\nif [ -d ~/.openviking/log ]; then\n    cp -r ~/.openviking/log ${BACKUP_DIR}/ 2>/dev/null || true\nfi\n\necho \"✓ Backup created at: ${BACKUP_DIR}\"\nls -lh ${BACKUP_DIR}/\n\n# ==========================================\n# STEP 3: CONFIRM DELETION\n# ==========================================\necho \"\"\necho \"==========================================\"\necho \"WARNING: ABOUT TO DELETE ALL DATA!\"\necho \"==========================================\"\necho \"Workspace to delete: ${WORKSPACE}\"\necho \"Backup location: ${BACKUP_DIR}\"\necho \"\"\nread -p \"Type 'DELETE' to confirm data removal: \" CONFIRM\n\nif [ \"${CONFIRM}\" != \"DELETE\" ]; then\n    echo \"Cleanup cancelled. Backup preserved at ${BACKUP_DIR}\"\n    exit 0\nfi\n\n# ==========================================\n# STEP 4: DELETE DATA\n# ==========================================\necho \"\"\necho \"Step 4: Deleting data...\"\n\n# Delete workspace\nif [ -d \"${WORKSPACE}\" ]; then\n    echo \"Deleting workspace: ${WORKSPACE}\"\n    rm -rf \"${WORKSPACE}\"\nfi\n\n# Optional: Delete old log (uncomment if needed)\n# echo \"Deleting old log...\"\n# rm -rf ~/.openviking/log/*\n\n# Cleanup any temporary files\nrm -f ~/.openviking/server.pid\n\necho \"✓ Data deleted successfully\"\n\n# ==========================================\n# STEP 5: COMPLETION\n# ==========================================\necho \"\"\necho \"==========================================\"\necho \"Cleanup Complete!\"\necho \"==========================================\"\necho \"Backup preserved at: ${BACKUP_DIR}\"\necho \"\"\necho \"Next steps:\"\necho \"1. Reconfigure ov.conf if needed\"\necho \"2. Start the server: openviking-server\"\necho \"3. Verify with: curl http://localhost:1933/health\"\necho \"\"\necho \"To restore from backup:\"\necho \"  tar -xzf ${BACKUP_DIR}/workspace_backup.tar.gz -C $(dirname ${WORKSPACE})\"\n```\n\n### Quick Cleanup (for Development Only)\n\n```bash\n# WARNING: Only use in development!\n# No backup created - data loss guaranteed!\n\n# 1. Stop server\npkill -f openviking-server\nsleep 2\npkill -9 -f openviking-server 2>/dev/null || true\n\n# 2. Delete workspace (adjust path as needed)\nrm -rf ~/.openviking/data\n\n# 3. Cleanup PID and temp files\nrm -f ~/.openviking/server.pid\n\necho \"Quick cleanup complete\"\n```\n\n## Verification and Troubleshooting\n\n### Health Check Verification\n\n```bash\n# Basic health check (always available)\ncurl http://localhost:1933/health\n# Expected: {\"status\": \"ok\"}\n\n# Readiness check (verifies all components)\ncurl http://localhost:1933/ready\n# Expected: {\"status\": \"ready\", \"checks\": {\"agfs\": \"ok\", \"vectordb\": \"ok\", \"api_key_manager\": \"ok\"}}\n\n# System status via CLI (~/.openviking/ovcli.conf should be configured)\nov status\n```\n\n### Common Issues and Solutions\n\n#### Issue: Server won't start\n\n**Check:**\n```bash\n# 1. Check if port is in use\nlsof -i :1933\nnetstat -tulpn | grep 1933\n\n# 2. Check log for errors\ntail -10 ~/.openviking/log/openviking-server.log\ntail -100 ~/.openviking/log/openviking.log\n\n\n# 3. Verify config file is valid JSON\npython3 -c 'import json, os; json.load(open(os.path.expanduser(\"~/.openviking/ov.conf\"))); print(\"Config is valid\")'\n\n# 4. Verify virtual environment\nsource ~/.openviking/ov-venv/bin/activate\nwhich openviking-server\npip list | grep openviking\n```\n\n**Solution:**\n```bash\n# If port conflict: kill the process or use different port\nlsof -ti :1933 | xargs kill -9 2>/dev/null || true\n\n# Or start on different port\nnohup openviking-server --port 1934 > ~/.openviking/log/openviking-server.log 2>&1 &\n```\n\n#### Issue: API Key Errors\n\n**Check:**\n```bash\n# Verify API keys in config\npython3 -c '\nimport json, os\ncfg = json.load(open(os.path.expanduser(\"~/.openviking/ov.conf\")))\nprint(\"Embedding provider:\", cfg.get(\"embedding\", {}).get(\"dense\", {}).get(\"provider\"))\nprint(\"VLM provider:\", cfg.get(\"vlm\", {}).get(\"provider\"))\nprint(\"API keys set:\", bool(cfg.get(\"embedding\", {}).get(\"dense\", {}).get(\"api_key\")), bool(cfg.get(\"vlm\", {}).get(\"api_key\")))\n'\n```\n\n**Solution:** Verify API keys are correct and have the required permissions. Check network connectivity to the model provider endpoints. Ensure API keys are not expired.\n\n## Prerequisites\n\n- Python 3.10+ installed\n- uv package manager available\n- Sufficient disk space for workspace and log\n- API keys for embedding and VLM models configured\n- Network access to model providers (if using cloud models)\n","frontmatter":{"name":"ov-server-operate","description":"Operate and maintain OpenViking server - configure, install, start, stop, and cleanup the server. Use when need to setup or manage OpenViking service deployment.","compatibility":"OpenViking CLI configured at `~/.openviking/ovcli.conf`"},"isInternal":false,"tokens":3072,"sizeBytes":11553},{"name":"plugin.json","path":"examples/claude-code-memory-plugin/.claude-plugin/plugin.json","rawUrl":"https://raw.githubusercontent.com/volcengine/OpenViking/HEAD/examples/claude-code-memory-plugin/.claude-plugin/plugin.json","title":"claude-plugin Plugin Manifest","category":"plugin-manifest","format":"json","content":"# claude-plugin Plugin Manifest\n\nPath: `examples/claude-code-memory-plugin/.claude-plugin/plugin.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/volcengine/OpenViking/HEAD/examples/claude-code-memory-plugin/.claude-plugin/plugin.json)","tokens":300,"sizeBytes":256},{"name":"plugin.json","path":"examples/codex-memory-plugin/.codex-plugin/plugin.json","rawUrl":"https://raw.githubusercontent.com/volcengine/OpenViking/HEAD/examples/codex-memory-plugin/.codex-plugin/plugin.json","title":"codex-plugin Plugin Manifest","category":"plugin-manifest","format":"json","content":"# codex-plugin Plugin Manifest\n\nPath: `examples/codex-memory-plugin/.codex-plugin/plugin.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/volcengine/OpenViking/HEAD/examples/codex-memory-plugin/.codex-plugin/plugin.json)","tokens":300,"sizeBytes":241},{"name":"openclaw.plugin.json","path":"examples/openclaw-plugin/openclaw.plugin.json","rawUrl":"https://raw.githubusercontent.com/volcengine/OpenViking/HEAD/examples/openclaw-plugin/openclaw.plugin.json","title":"openclaw-plugin Plugin Manifest","category":"plugin-manifest","format":"json","content":"# openclaw-plugin Plugin Manifest\n\nPath: `examples/openclaw-plugin/openclaw.plugin.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/volcengine/OpenViking/HEAD/examples/openclaw-plugin/openclaw.plugin.json)","tokens":300,"sizeBytes":226}],"systemPromptSnippet":"<yakaai_skills repo=\"volcengine/OpenViking\">\n<!-- File: bot/workspace/skills/cron/SKILL.md (Tokens: ~306 | Category: anthropic-skill) -->\n---\nname: cron\ndescription: Schedule reminders and recurring tasks.\n---\n\n# Cron\n\nUse the `cron` tool to schedule reminders or recurring tasks.\n\n## Three Modes\n\n1. **Reminder** - message is sent directly to user\n2. **Task** - message is a task description, agent executes and sends result\n3. **One-time** - runs once at a specific time, then auto-deletes\n\n## Examples\n\nFixed reminder:\n```\ncron(action=\"add\", message=\"Time to take a break!\", every_seconds=1200)\n```\n\nDynamic task (agent executes each time):\n```\ncron(action=\"add\", message=\"Check HKUDS/vikingbot GitHub stars and report\", every_seconds=600)\n```\n\nOne-time scheduled task (compute ISO datetime from current time):\n```\ncron(action=\"add\", message=\"Remind me about the meeting\", at=\"<ISO datetime>\")\n```\n\nList/remove:\n```\ncron(action=\"list\")\ncron(action=\"remove\", job_id=\"abc123\")\n```\n\n## Time Expressions\n\n| User says | Parameters |\n|-----------|------------|\n| every 20 minutes | every_seconds: 1200 |\n| every hour | every_seconds: 3600 |\n| every day at 8am | cron_expr: \"0 8 * * *\" |\n| weekdays at 5pm | cron_expr: \"0 17 * * 1-5\" |\n| at a specific time | at: ISO datetime string (compute from current time) |\n\n<!-- File: bot/workspace/skills/github-proxy/SKILL.md (Tokens: ~711 | Category: anthropic-skill) -->\n---\nname: github-proxy\ndescription: GitHub 国内访问加速 skill，使用 githubproxy.cc 代理加速 GitHub 仓库克隆、文件下载、Raw 文件访问等操作。使用场景：(1) 需要 git clone GitHub 仓库时加速，(2) 下载 GitHub Release 文件、Raw 文件、Archive 压缩包时加速，(3) 任何需要访问 GitHub 资源但速度慢的场景\n---\n\n# GitHub 国内代理加速 Skill\n\n使用 githubproxy.cc 代理服务，为国内访问 GitHub 提供加速支持。\n\n## 代理服务\n\n当前使用的代理服务：\n- **主要服务**: githubproxy.cc (测试有效，加速约 3 倍)\n- **备用服务**: ghfast.top\n\n## 使用方法\n\n### 1. Git Clone 加速\n\n将 GitHub 仓库链接前加上 `https://githubproxy.cc/` 前缀：\n\n```bash\n# 原始链接\ngit clone https://github.com/username/repo.git\n\n# 加速链接\ngit clone https://githubproxy.cc/https://github.com/username/repo.git\n```\n\n### 2. 文件下载加速\n\n支持以下类型的 GitHub 资源加速：\n\n- **Raw 文件**: `https://raw.githubusercontent.com/...`\n- **Release 文件**: 项目发布的附件\n- **Archive 压缩包**: 仓库打包下载\n- **Gist 文件**: `gist.github.com` 或 `gist.githubusercontent.com`\n\n```bash\n# 原始链接\nwget https://raw.githubusercontent.com/username/repo/main/file.txt\n\n# 加速链接\nwget https://githubproxy.cc/https://raw.githubusercontent.com/username/repo/main/file.txt\n```\n\n### 3. 使用辅助脚本\n\n使用 `scripts/convert_url.py` 自动转换 GitHub 链接：\n\n```bash\npython scripts/convert_url.py \"https://github.com/username/repo.git\"\n```\n\n## 链接转换规则\n\n| 原始链接格式 | 转换后格式 |\n|-------------|-----------|\n| `https://github.com/username/repo.git` | `https://githubproxy.cc/https://github.com/username/repo.git` |\n| `https://raw.githubusercontent.com/...` | `https://githubproxy.cc/https://raw.githubusercontent.com/...` |\n| `https://github.com/.../releases/download/...` | `https://githubproxy.cc/https://github.com/.../releases/download/...` |\n| `https://github.com/.../archive/...` | `https://githubproxy.cc/https://github.com/.../archive/...` |\n\n## 注意事项\n\n- 本服务仅供学习研究使用，请勿滥用\n- 如果 githubproxy.cc 不可用，请尝试备用服务 ghfast.top\n- 不支持 SSH Key 方式的 git clone\n- Push、PR、Issue 等操作建议直接使用官方 GitHub 地址\n\n<!-- File: bot/workspace/skills/github/SKILL.md (Tokens: ~382 | Category: anthropic-skill) -->\n---\nname: github\ndescription: \"Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.\"\nmetadata: {\"vikingbot\":{\"emoji\":\"🐙\",\"requires\":{\"bins\":[\"gh\"]},\"install\":[{\"id\":\"brew\",\"kind\":\"brew\",\"formula\":\"gh\",\"bins\":[\"gh\"],\"label\":\"Install GitHub CLI (brew)\"},{\"id\":\"apt\",\"kind\":\"apt\",\"package\":\"gh\",\"bins\":[\"gh\"],\"label\":\"Install GitHub CLI (apt)\"}]}}\n---\n\n# GitHub Skill\n\nUse the `gh` CLI to interact with GitHub. Always specify `--repo owner/repo` when not in a git directory, or use URLs directly.\n\n## Pull Requests\n\nCheck CI status on a PR:\n```bash\ngh pr checks 55 --repo owner/repo\n```\n\nList recent workflow runs:\n```bash\ngh run list --repo owner/repo --limit 10\n```\n\nView a run and see which steps failed:\n```bash\ngh run view <run-id> --repo owner/repo\n```\n\nView logs for failed steps only:\n```bash\ngh run view <run-id> --repo owner/repo --log-failed\n```\n\n## API for Advanced Queries\n\nThe `gh api` command is useful for accessing data not available through other subcommands.\n\nGet PR with specific fields:\n```bash\ngh api repos/owner/repo/pulls/55 --jq '.title, .state, .user.login'\n```\n\n## JSON Output\n\nMost commands support `--json` for structured output.  You can use `--jq` to filter:\n\n```bash\ngh issue list --repo owner/repo --json number,title --jq '.[] | \"\\(.number): \\(.title)\"'\n```\n\n<!-- File: bot/workspace/skills/opencode/SKILL.md (Tokens: ~216 | Category: anthropic-skill) -->\n---\nname: opencode\ndescription: Monitor and manage your OpenCode tasks using helper scripts.\nmetadata: {\"vikingbot\":{\"emoji\":\"💻\",\"requires\":{\"bins\":[\"python3\"]}}}\n---\n\n# OpenCode Skill\n\nUse helper scripts to manage your OpenCode instances.\n\n## Helper Scripts\n\nAll scripts are in the workspace skills directory. Run them with:\n```bash\nuv run python skills/opencode/script_name.py\n```\n\nNote: Do not kill the opencode process.\n\n## Scripts\n\n### `list_sessions.py` \nListing all OpenCode sessions\n\nExample:\nuv run python skills/opencode/list_sessions.py\n\n\n### `list_messages_of_session.py`\nListing latest OpenCode messages by session_id \n\nExample:\nuv run python skills/opencode/list_sessions.py {session_id}\n\n## Session Status Types\n\n- **🟢 WAITING**: Last message was from user - agent is waiting for input\n- **🔴 WORKING**: Last message was from assistant - agent recently finished or may be working\n- **🟡 UNKNOWN**: Cannot determine status\n\n<!-- File: bot/workspace/skills/opencode/status.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Opencode Skill\n\nPath: `bot/workspace/skills/opencode/status.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/volcengine/OpenViking/HEAD/bot/workspace/skills/opencode/status.json)\n\n<!-- File: bot/workspace/skills/README.md (Tokens: ~300 | Category: anthropic-skill) -->\n# Skills Skill\n\nPath: `bot/workspace/skills/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/volcengine/OpenViking/HEAD/bot/workspace/skills/README.md)\n\n<!-- File: bot/workspace/skills/skill-creator/SKILL.md (Tokens: ~3801 | Category: anthropic-skill) -->\n---\nname: skill-creator\ndescription: Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.\n---\n\n# Skill Creator\n\nThis skill provides guidance for creating effective skills.\n\n## About Skills\n\nSkills are modular, self-contained packages that extend the agent's capabilities by providing\nspecialized knowledge, workflows, and tools. Think of them as \"onboarding guides\" for specific\ndomains or tasks—they transform the agent from a general-purpose agent into a specialized agent\nequipped with procedural knowledge that no model can fully possess.\n\n### What Skills Provide\n\n1. Specialized workflows - Multi-step procedures for specific domains\n2. Tool integrations - Instructions for working with specific file formats or APIs\n3. Domain expertise - Company-specific knowledge, schemas, business logic\n4. Bundled resources - Scripts, references, and assets for complex and repetitive tasks\n\n## Core Principles\n\n### Concise is Key\n\nThe context window is a public good. Skills share the context window with everything else the agent needs: system prompt, conversation history, other Skills' metadata, and the actual user request.\n\n**Default assumption: the agent is already very smart.** Only add context the agent doesn't already have. Challenge each piece of information: \"Does the agent really need this explanation?\" and \"Does this paragraph justify its token cost?\"\n\nPrefer concise examples over verbose explanations.\n\n### Set Appropriate Degrees of Freedom\n\nMatch the level of specificity to the task's fragility and variability:\n\n**High freedom (text-based instructions)**: Use when multiple approaches are valid, decisions depend on context, or heuristics guide the approach.\n\n**Medium freedom (pseudocode or scripts with parameters)**: Use when a preferred pattern exists, some variation is acceptable, or configuration affects behavior.\n\n**Low freedom (specific scripts, few parameters)**: Use when operations are fragile and error-prone, consistency is critical, or a specific sequence must be followed.\n\nThink of the agent as exploring a path: a narrow bridge with cliffs needs specific guardrails (low freedom), while an open field allows many routes (high freedom).\n\n### Anatomy of a Skill\n\nEvery skill consists of a required SKILL.md file and optional bundled resources:\n\n```\nskill-name/\n├── SKILL.md (required)\n│   ├── YAML frontmatter metadata (required)\n│   │   ├── name: (required)\n│   │   └── description: (required)\n│   └── Markdown instructions (required)\n└── Bundled Resources (optional)\n    ├── scripts/          - Executable code (Python/Bash/etc.)\n    ├── references/       - Documentation intended to be loaded into context as needed\n    └── assets/           - Files used in output (templates, icons, fonts, etc.)\n```\n\n#### SKILL.md (required)\n\nEvery SKILL.md consists of:\n\n- **Frontmatter** (YAML): Contains `name` and `description` fields. These are the only fields that the agent reads to determine when the skill gets used, thus it is very important to be clear and comprehensive in describing what the skill is, and when it should be used.\n- **Body** (Markdown): Instructions and guidance for using the skill. Only loaded AFTER the skill triggers (if at all).\n\n#### Bundled Resources (optional)\n\n##### Scripts (`scripts/`)\n\nExecutable code (Python/Bash/etc.) for tasks that require deterministic reliability or are repeatedly rewritten.\n\n- **When to include**: When the same code is being rewritten repeatedly or deterministic reliability is needed\n- **Example**: `scripts/rotate_pdf.py` for PDF rotation tasks\n- **Benefits**: Token efficient, deterministic, may be executed without loading into context\n- **Note**: Scripts may still need to be read by the agent for patching or environment-specific adjustments\n\n##### References (`references/`)\n\nDocumentation and reference material intended to be loaded as needed into context to inform the agent's process and thinking.\n\n- **When to include**: For documentation that the agent should reference while working\n- **Examples**: `references/finance.md` for financial schemas, `references/mnda.md` for company NDA template, `references/policies.md` for company policies, `references/api_docs.md` for API specifications\n- **Use cases**: Database schemas, API documentation, domain knowledge, company policies, detailed workflow guides\n- **Benefits**: Keeps SKILL.md lean, loaded only when the agent determines it's needed\n- **Best practice**: If files are large (>10k words), include grep search patterns in SKILL.md\n- **Avoid duplication**: Information should live in either SKILL.md or references files, not both. Prefer references files for detailed information unless it's truly core to the skill—this keeps SKILL.md lean while making information discoverable without hogging the context window. Keep only essential procedural instructions and workflow guidance in SKILL.md; move detailed reference material, schemas, and examples to references files.\n\n##### Assets (`assets/`)\n\nFiles not intended to be loaded into context, but rather used within the output the agent produces.\n\n- **When to include**: When the skill needs files that will be used in the final output\n- **Examples**: `assets/logo.png` for brand assets, `assets/slides.pptx` for PowerPoint templates, `assets/frontend-template/` for HTML/React boilerplate, `assets/font.ttf` for typography\n- **Use cases**: Templates, images, icons, boilerplate code, fonts, sample documents that get copied or modified\n- **Benefits**: Separates output resources from documentation, enables the agent to use files without loading them into context\n\n#### What to Not Include in a Skill\n\nA skill should only contain essential files that directly support its functionality. Do NOT create extraneous documentation or auxiliary files, including:\n\n- README.md\n- INSTALLATION_GUIDE.md\n- QUICK_REFERENCE.md\n- CHANGELOG.md\n- etc.\n\nThe skill should only contain the information needed for an AI agent to do the job at hand. It should not contain auxiliary context about the process that went into creating it, setup and testing procedures, user-facing documentation, etc. Creating additional documentation files just adds clutter and confusion.\n\n### Progressive Disclosure Design Principle\n\nSkills use a three-level loading system to manage context efficiently:\n\n1. **Metadata (name + description)** - Always in context (~100 words)\n2. **SKILL.md body** - When skill triggers (<5k words)\n3. **Bundled resources** - As needed by the agent (Unlimited because scripts can be executed without reading into context window)\n\n#### Progressive Disclosure Patterns\n\nKeep SKILL.md body to the essentials and under 500 lines to minimize context bloat. Split content into separate files when approaching this limit. When splitting out content into other files, it is very important to reference them from SKILL.md and describe clearly when to read them, to ensure the reader of the skill knows they exist and when to use them.\n\n**Key principle:** When a skill supports multiple variations, frameworks, or options, keep only the core workflow and selection guidance in SKILL.md. Move variant-specific details (patterns, examples, configuration) into separate reference files.\n\n**Pattern 1: High-level guide with references**\n\n```markdown\n# PDF Processing\n\n## Quick start\n\nExtract text with pdfplumber:\n[code example]\n\n## Advanced features\n\n- **Form filling**: See [FORMS.md](FORMS.md) for complete guide\n- **API reference**: See [REFERENCE.md](REFERENCE.md) for all methods\n- **Examples**: See [EXAMPLES.md](EXAMPLES.md) for common patterns\n```\n\nthe agent loads FORMS.md, REFERENCE.md, or EXAMPLES.md only when needed.\n\n**Pattern 2: Domain-specific organization**\n\nFor Skills with multiple domains, organize content by domain to avoid loading irrelevant context:\n\n```\nbigquery-skill/\n├── SKILL.md (overview and navigation)\n└── reference/\n    ├── finance.md (revenue, billing metrics)\n    ├── sales.md (opportunities, pipeline)\n    ├── product.md (API usage, features)\n    └── marketing.md (campaigns, attribution)\n```\n\nWhen a user asks about sales metrics, the agent only reads sales.md.\n\nSimilarly, for skills supporting multiple frameworks or variants, organize by variant:\n\n```\ncloud-deploy/\n├── SKILL.md (workflow + provider selection)\n└── references/\n    ├── aws.md (AWS deployment patterns)\n    ├── gcp.md (GCP deployment patterns)\n    └── azure.md (Azure deployment patterns)\n```\n\nWhen the user chooses AWS, the agent only reads aws.md.\n\n**Pattern 3: Conditional details**\n\nShow basic content, link to advanced content:\n\n```markdown\n# DOCX Processing\n\n## Creating documents\n\nUse docx-js for new documents. See [DOCX-JS.md](DOCX-JS.md).\n\n## Editing documents\n\nFor simple edits, modify the XML directly.\n\n**For tracked changes**: See [REDLINING.md](REDLINING.md)\n**For OOXML details**: See [OOXML.md](OOXML.md)\n```\n\nthe agent reads REDLINING.md or OOXML.md only when the user needs those features.\n\n**Important guidelines:**\n\n- **Avoid deeply nested references** - Keep references one level deep from SKILL.md. All reference files should link directly from SKILL.md.\n- **Structure longer reference files** - For files longer than 100 lines, include a table of contents at the top so the agent can see the full scope when previewing.\n\n## Skill Creation Process\n\nSkill creation involves these steps:\n\n1. Understand the skill with concrete examples\n2. Plan reusable skill contents (scripts, references, assets)\n3. Initialize the skill (run init_skill.py)\n4. Edit the skill (implement resources and write SKILL.md)\n5. Package the skill (run package_skill.py)\n6. Iterate based on real usage\n\nFollow these steps in order, skipping only if there is a clear reason why they are not applicable.\n\n### Skill Naming\n\n- Use lowercase letters, digits, and hyphens only; normalize user-provided titles to hyphen-case (e.g., \"Plan Mode\" -> `plan-mode`).\n- When generating names, generate a name under 64 characters (letters, digits, hyphens).\n- Prefer short, verb-led phrases that describe the action.\n- Namespace by tool when it improves clarity or triggering (e.g., `gh-address-comments`, `linear-address-issue`).\n- Name the skill folder exactly after the skill name.\n\n### Step 1: Understanding the Skill with Concrete Examples\n\nSkip this step only when the skill's usage patterns are already clearly understood. It remains valuable even when working with an existing skill.\n\nTo create an effective skill, clearly understand concrete examples of how the skill will be used. This understanding can come from either direct user examples or generated examples that are validated with user feedback.\n\nFor example, when building an image-editor skill, relevant questions include:\n\n- \"What functionality should the image-editor skill support? Editing, rotating, anything else?\"\n- \"Can you give some examples of how this skill would be used?\"\n- \"I can imagine users asking for things like 'Remove the red-eye from this image' or 'Rotate this image'. Are there other ways you imagine this skill being used?\"\n- \"What would a user say that should trigger this skill?\"\n\nTo avoid overwhelming users, avoid asking too many questions in a single message. Start with the most important questions and follow up as needed for better effectiveness.\n\nConclude this step when there is a clear sense of the functionality the skill should support.\n\n### Step 2: Planning the Reusable Skill Contents\n\nTo turn concrete examples into an effective skill, analyze each example by:\n\n1. Considering how to execute on the example from scratch\n2. Identifying what scripts, references, and assets would be helpful when executing these workflows repeatedly\n\nExample: When building a `pdf-editor` skill to handle queries like \"Help me rotate this PDF,\" the analysis shows:\n\n1. Rotating a PDF requires re-writing the same code each time\n2. A `scripts/rotate_pdf.py` script would be helpful to store in the skill\n\nExample: When designing a `frontend-webapp-builder` skill for queries like \"Build me a todo app\" or \"Build me a dashboard to track my steps,\" the analysis shows:\n\n1. Writing a frontend webapp requires the same boilerplate HTML/React each time\n2. An `assets/hello-world/` template containing the boilerplate HTML/React project files would be helpful to store in the skill\n\nExample: When building a `big-query` skill to handle queries like \"How many users have logged in today?\" the analysis shows:\n\n1. Querying BigQuery requires re-discovering the table schemas and relationships each time\n2. A `references/schema.md` file documenting the table schemas would be helpful to store in the skill\n\nTo establish the skill's contents, analyze each concrete example to create a list of the reusable resources to include: scripts, references, and assets.\n\n### Step 3: Initializing the Skill\n\nAt this point, it is time to actually create the skill.\n\nSkip this step only if the skill being developed already exists, and iteration or packaging is needed. In this case, continue to the next step.\n\nWhen creating a new skill from scratch, always run the `init_skill.py` script. The script conveniently generates a new template skill directory that automatically includes everything a skill requires, making the skill creation process much more efficient and reliable.\n\nUsage:\n\n```bash\nscripts/init_skill.py <skill-name> --path <output-directory> [--resources scripts,references,assets] [--examples]\n```\n\nExamples:\n\n```bash\nscripts/init_skill.py my-skill --path skills/public\nscripts/init_skill.py my-skill --path skills/public --resources scripts,references\nscripts/init_skill.py my-skill --path skills/public --resources scripts --examples\n```\n\nThe script:\n\n- Creates the skill directory at the specified path\n- Generates a SKILL.md template with proper frontmatter and TODO placeholders\n- Optionally creates resource directories based on `--resources`\n- Optionally adds example files when `--examples` is set\n\nAfter initialization, customize the SKILL.md and add resources as needed. If you used `--examples`, replace or delete placeholder files.\n\n### Step 4: Edit the Skill\n\nWhen editing the (newly-generated or existing) skill, remember that the skill is being created for another instance of the agent to use. Include information that would be beneficial and non-obvious to the agent. Consider what procedural knowledge, domain-specific details, or reusable assets would help another the agent instance execute these tasks more effectively.\n\n#### Learn Proven Design Patterns\n\nConsult these helpful guides based on your skill's needs:\n\n- **Multi-step processes**: See references/workflows.md for sequential workflows and conditional logic\n- **Specific output formats or quality standards**: See references/output-patterns.md for template and example patterns\n\nThese files contain established best practices for effective skill design.\n\n#### Start with Reusable Skill Contents\n\nTo begin implementation, start with the reusable resources identified above: `scripts/`, `references/`, and `assets/` files. Note that this step may require user input. For example, when implementing a `brand-guidelines` skill, the user may need to provide brand assets or templates to store in `assets/`, or documentation to store in `references/`.\n\nAdded scripts must be tested by actually running them to ensure there are no bugs and that the output matches what is expected. If there are many similar scripts, only a representative sample needs to be tested to ensure confidence that they all work while balancing time to completion.\n\nIf you used `--examples`, delete any placeholder files that are not needed for the skill. Only create resource directories that are actually required.\n\n#### Update SKILL.md\n\n**Writing Guidelines:** Always use imperative/infinitive form.\n\n##### Frontmatter\n\nWrite the YAML frontmatter with `name` and `description`:\n\n- `name`: The skill name\n- `description`: This is the primary triggering mechanism for your skill, and helps the agent understand when to use the skill.\n  - Include both what the Skill does and specific triggers/contexts for when to use it.\n  - Include all \"when to use\" information here - Not in the body. The body is only loaded after triggering, so \"When to Use This Skill\" sections in the body are not helpful to the agent.\n  - Example description for a `docx` skill: \"Comprehensive document creation, editing, and analysis with support for tracked changes, comments, formatting preservation, and text extraction. Use when the agent needs to work with professional documents (.docx files) for: (1) Creating new documents, (2) Modifying or editing content, (3) Working with tracked changes, (4) Adding comments, or any other document tasks\"\n\nDo not include any other fields in YAML frontmatter.\n\n##### Body\n\nWrite instructions for using the skill and its bundled resources.\n\n### Step 5: Packaging a Skill\n\nOnce development of the skill is complete, it must be packaged into a distributable .skill file that gets shared with the user. The packaging process automatically validates the skill first to ensure it meets all requirements:\n\n```bash\nscripts/package_skill.py <path/to/skill-folder>\n```\n\nOptional output directory specification:\n\n```bash\nscripts/package_skill.py <path/to/skill-folder> ./dist\n```\n\nThe packaging script will:\n\n1. **Validate** the skill automatically, checking:\n\n   - YAML frontmatter format and required fields\n   - Skill naming conventions and directory structure\n   - Description completeness and quality\n   - File organization and resource references\n\n2. **Package** the skill if validation passes, creating a .skill file named after the skill (e.g., `my-skill.skill`) that includes all files and maintains the proper directory structure for distribution. The .skill file is a zip file with a .skill extension.\n\nIf validation fails, the script will report the errors and exit without creating a package. Fix any validation errors and run the packaging command again.\n\n### Step 6: Iterate\n\nAfter testing the skill, users may request improvements. Often this happens right after using the skill, with fresh context of how the skill performed.\n\n**Iteration workflow:**\n\n1. Use the skill on real tasks\n2. Notice struggles or inefficiencies\n3. Identify how SKILL.md or bundled resources should be updated\n4. Implement changes and test again\n\n<!-- File: bot/workspace/skills/summarize/SKILL.md (Tokens: ~581 | Category: anthropic-skill) -->\n---\nname: summarize\ndescription: Summarize or extract text/transcripts from URLs, podcasts, and local files (great fallback for “transcribe this YouTube/video”).\nhomepage: https://summarize.sh\nmetadata: {\"vikingbot\":{\"emoji\":\"🧾\",\"requires\":{\"bins\":[\"summarize\"]},\"install\":[{\"id\":\"brew\",\"kind\":\"brew\",\"formula\":\"steipete/tap/summarize\",\"bins\":[\"summarize\"],\"label\":\"Install summarize (brew)\"}]}}\n---\n\n# Summarize\n\nFast CLI to summarize URLs, local files, and YouTube links.\n\n## When to use (trigger phrases)\n\nUse this skill immediately when the user asks any of:\n- “use summarize.sh”\n- “what’s this link/video about?”\n- “summarize this URL/article”\n- “transcribe this YouTube/video” (best-effort transcript extraction; no `yt-dlp` needed)\n\n## Quick start\n\n```bash\nsummarize \"https://example.com\" --model google/gemini-3-flash-preview\nsummarize \"/path/to/file.pdf\" --model google/gemini-3-flash-preview\nsummarize \"https://youtu.be/dQw4w9WgXcQ\" --youtube auto\n```\n\n## YouTube: summary vs transcript\n\nBest-effort transcript (URLs only):\n\n```bash\nsummarize \"https://youtu.be/dQw4w9WgXcQ\" --youtube auto --extract-only\n```\n\nIf the user asked for a transcript but it’s huge, return a tight summary first, then ask which section/time range to expand.\n\n## Model + keys\n\nSet the API key for your chosen provider:\n- OpenAI: `OPENAI_API_KEY`\n- Anthropic: `ANTHROPIC_API_KEY`\n- xAI: `XAI_API_KEY`\n- Google: `GEMINI_API_KEY` (aliases: `GOOGLE_GENERATIVE_AI_API_KEY`, `GOOGLE_API_KEY`)\n\nDefault model is `google/gemini-3-flash-preview` if none is set.\n\n## Useful flags\n\n- `--length short|medium|long|xl|xxl|<chars>`\n- `--max-output-tokens <count>`\n- `--extract-only` (URLs only)\n- `--json` (machine readable)\n- `--firecrawl auto|off|always` (fallback extraction)\n- `--youtube auto` (Apify fallback if `APIFY_API_TOKEN` set)\n\n## Config\n\nOptional config file: `~/.summarize/config.json`\n\n```json\n{ \"model\": \"openai/gpt-5.2\" }\n```\n\nOptional services:\n- `FIRECRAWL_API_KEY` for blocked sites\n- `APIFY_API_TOKEN` for YouTube fallback\n\n<!-- File: bot/workspace/skills/tmux/SKILL.md (Tokens: ~1259 | Category: anthropic-skill) -->\n---\nname: tmux\ndescription: Remote-control tmux sessions for interactive CLIs by sending keystrokes and scraping pane output.\nmetadata: {\"vikingbot\":{\"emoji\":\"🧵\",\"os\":[\"darwin\",\"linux\"],\"requires\":{\"bins\":[\"tmux\"]}}}\n---\n\n# tmux Skill\n\nUse tmux only when you need an interactive TTY. Prefer exec background mode for long-running, non-interactive tasks.\n\n## Quickstart (isolated socket, exec tool)\n\n```bash\nSOCKET_DIR=\"${NANOBOT_TMUX_SOCKET_DIR:-${TMPDIR:-/tmp}/vikingbot-tmux-sockets}\"\nmkdir -p \"$SOCKET_DIR\"\nSOCKET=\"$SOCKET_DIR/vikingbot.sock\"\nSESSION=vikingbot-python\n\ntmux -S \"$SOCKET\" new -d -s \"$SESSION\" -n shell\ntmux -S \"$SOCKET\" send-keys -t \"$SESSION\":0.0 -- 'PYTHON_BASIC_REPL=1 python3 -q' Enter\ntmux -S \"$SOCKET\" capture-pane -p -J -t \"$SESSION\":0.0 -S -200\n```\n\nAfter starting a session, always print monitor commands:\n\n```\nTo monitor:\n  tmux -S \"$SOCKET\" attach -t \"$SESSION\"\n  tmux -S \"$SOCKET\" capture-pane -p -J -t \"$SESSION\":0.0 -S -200\n```\n\n## Socket convention\n\n- Use `NANOBOT_TMUX_SOCKET_DIR` environment variable.\n- Default socket path: `\"$NANOBOT_TMUX_SOCKET_DIR/vikingbot.sock\"`.\n\n## Targeting panes and naming\n\n- Target format: `session:window.pane` (defaults to `:0.0`).\n- Keep names short; avoid spaces.\n- Inspect: `tmux -S \"$SOCKET\" list-sessions`, `tmux -S \"$SOCKET\" list-panes -a`.\n\n## Finding sessions\n\n- List sessions on your socket: `{baseDir}/scripts/find-sessions.sh -S \"$SOCKET\"`.\n- Scan all sockets: `{baseDir}/scripts/find-sessions.sh --all` (uses `NANOBOT_TMUX_SOCKET_DIR`).\n\n## Sending input safely\n\n- Prefer literal sends: `tmux -S \"$SOCKET\" send-keys -t target -l -- \"$cmd\"`.\n- Control keys: `tmux -S \"$SOCKET\" send-keys -t target C-c`.\n\n## Watching output\n\n- Capture recent history: `tmux -S \"$SOCKET\" capture-pane -p -J -t target -S -200`.\n- Wait for prompts: `{baseDir}/scripts/wait-for-text.sh -t session:0.0 -p 'pattern'`.\n- Attaching is OK; detach with `Ctrl+b d`.\n\n## Spawning processes\n\n- For python REPLs, set `PYTHON_BASIC_REPL=1` (non-basic REPL breaks send-keys flows).\n\n## Windows / WSL\n\n- tmux is supported on macOS/Linux. On Windows, use WSL and install tmux inside WSL.\n- This skill is gated to `darwin`/`linux` and requires `tmux` on PATH.\n\n## Orchestrating Coding Agents (Codex, Claude Code)\n\ntmux excels at running multiple coding agents in parallel:\n\n```bash\nSOCKET=\"${TMPDIR:-/tmp}/codex-army.sock\"\n\n# Create multiple sessions\nfor i in 1 2 3 4 5; do\n  tmux -S \"$SOCKET\" new-session -d -s \"agent-$i\"\ndone\n\n# Launch agents in different workdirs\ntmux -S \"$SOCKET\" send-keys -t agent-1 \"cd /tmp/project1 && codex --yolo 'Fix bug X'\" Enter\ntmux -S \"$SOCKET\" send-keys -t agent-2 \"cd /tmp/project2 && codex --yolo 'Fix bug Y'\" Enter\n\n# Poll for completion (check if prompt returned)\nfor sess in agent-1 agent-2; do\n  if tmux -S \"$SOCKET\" capture-pane -p -t \"$sess\" -S -3 | grep -q \"❯\"; then\n    echo \"$sess: DONE\"\n  else\n    echo \"$sess: Running...\"\n  fi\ndone\n\n# Get full output from completed session\ntmux -S \"$SOCKET\" capture-pane -p -t agent-1 -S -500\n```\n\n**Tips:**\n- Use separate git worktrees for parallel fixes (no branch conflicts)\n- `pnpm install` first before running codex in fresh clones\n- Check for shell prompt (`❯` or `$`) to detect completion\n- Codex needs `--yolo` or `--full-auto` for non-interactive fixes\n\n## Cleanup\n\n- Kill a session: `tmux -S \"$SOCKET\" kill-session -t \"$SESSION\"`.\n- Kill all sessions on a socket: `tmux -S \"$SOCKET\" list-sessions -F '#{session_name}' | xargs -r -n1 tmux -S \"$SOCKET\" kill-session -t`.\n- Remove everything on the private socket: `tmux -S \"$SOCKET\" kill-server`.\n\n## Helper: wait-for-text.sh\n\n`{baseDir}/scripts/wait-for-text.sh` polls a pane for a regex (or fixed string) with a timeout.\n\n```bash\n{baseDir}/scripts/wait-for-text.sh -t session:0.0 -p 'pattern' [-F] [-T 20] [-i 0.5] [-l 2000]\n```\n\n- `-t`/`--target` pane target (required)\n- `-p`/`--pattern` regex to match (required); add `-F` for fixed string\n- `-T` timeout seconds (integer, default 15)\n- `-i` poll interval seconds (default 0.5)\n- `-l` history lines to search (integer, default 1000)\n\n<!-- File: bot/workspace/skills/weather/SKILL.md (Tokens: ~387 | Category: anthropic-skill) -->\n---\nname: weather\ndescription: Get current weather and forecasts (no API key required).\nhomepage: https://wttr.in/:help\nmetadata: {\"vikingbot\":{\"emoji\":\"🌤️\",\"requires\":{\"bins\":[\"curl\"]}}}\n---\n\n# Weather\n\nTwo free services, no API keys needed.\n\n## wttr.in (primary)\n\nQuick one-liner:\n```bash\ncurl -s \"wttr.in/London?format=3\"\n# Output: London: ⛅️ +8°C\n```\n\nCompact format:\n```bash\ncurl -s \"wttr.in/London?format=%l:+%c+%t+%h+%w\"\n# Output: London: ⛅️ +8°C 71% ↙5km/h\n```\n\nFull forecast:\n```bash\ncurl -s \"wttr.in/London?T\"\n```\n\nFormat codes: `%c` condition · `%t` temp · `%h` humidity · `%w` wind · `%l` location · `%m` moon\n\nTips:\n- URL-encode spaces: `wttr.in/New+York`\n- Airport codes: `wttr.in/JFK`\n- Units: `?m` (metric) `?u` (USCS)\n- Today only: `?1` · Current only: `?0`\n- PNG: `curl -s \"wttr.in/Berlin.png\" -o /tmp/weather.png`\n\n## Open-Meteo (fallback, JSON)\n\nFree, no key, good for programmatic use:\n```bash\ncurl -s \"https://api.open-meteo.com/v1/forecast?latitude=51.5&longitude=-0.12&current_weather=true\"\n```\n\nFind coordinates for a city, then query. Returns JSON with temp, windspeed, weathercode.\n\nDocs: https://open-meteo.com/en/docs\n\n<!-- File: examples/openclaw-plugin/skills/install-openviking-memory/SKILL.md (Tokens: ~10607 | Category: anthropic-skill) -->\n---\nname: install-openviking-memory\ndescription: >\n  Install and configure the OpenViking long-term memory plugin for OpenClaw via natural conversation.\n  Once installed, the plugin automatically captures facts from chats and recalls relevant context\n  before each reply (auto-capture + auto-recall, cross-session). Covers prerequisites, install through\n  OpenClaw's plugin manager (ov-install as backup), wizard-based configuration, slot activation,\n  gateway restart, verification, multi-tenant root keys, multi-instance and uninstall. The user needs\n  no CLI knowledge — the agent runs everything and only asks for a few values. Assumes the OpenViking\n  server is already running; it does NOT install the server. Trigger on: \"install OpenViking\",\n  \"set up memory\", \"configure memory plugin\", \"add long-term memory\", \"semantic memory\", \"RAG\",\n  \"帮我装 OpenViking\", \"配置记忆插件\", \"安装记忆功能\", \"接入 OpenViking\".\nversion: 2026.6.5\nmetadata:\n  openclaw:\n    requires:\n      bins:\n        - node\n        - openclaw\n  emoji: \"🦣\"\n  homepage: \"https://github.com/volcengine/OpenViking\"\ntags:\n  - install\n  - setup\n  - memory\n  - openviking\n  - rag\n  - long-term-memory\n---\n\n# Install OpenViking Memory — Agent Installation & Reference Skill\n\nYou are about to help a user install and configure the OpenViking memory plugin for OpenClaw.\nThe 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.\n\n**Read this entire document before starting. Follow every step in order. Do NOT skip steps.**\n\nThis skill is split into two halves:\n\n- **Part 1 (STEP 0–10): Natural-language installation walkthrough.** Use this on first install or reconfigure.\n- **Part 2: Reference.** Tools, config schema, multi-tenant, multi-instance, daily ops, uninstall, error recovery. Read on demand.\n\n> **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.\n\n---\n\n# Part 1 — Natural-Language Installation Walkthrough\n\n## STEP 0: Detect Language\n\nIf the user's first message contains Chinese characters, respond in **Chinese** throughout.\nOtherwise respond in **English**.\nAll user-facing messages below have (CN)/(EN) variants — use the matching one.\n\nDo NOT show this step to the user.\n\n---\n\n## STEP 1: Detect Operating System (silently)\n\nRun this command and remember the result. Do NOT show it to the user.\n\n```bash\nuname -s 2>/dev/null || echo WINDOWS\n```\n\n- Output contains `Darwin` → `OS=mac`\n- Output contains `Linux` → `OS=linux`\n- Output is `WINDOWS` or the command fails → `OS=windows`\n\n---\n\n## STEP 2: Check Prerequisites (silently)\n\nRun silently:\n\n```bash\nnode -v\nopenclaw --version\n```\n\n**If `node` is missing:**\n\n> (CN) 你的系统没有安装 Node.js。OpenClaw 和 OpenViking 插件需要 Node.js >= 22。请先安装 Node.js，然后再回来找我。\n> (EN) Node.js is not installed. OpenClaw and the OpenViking plugin require Node.js >= 22. Please install Node.js first, then come back.\n\n**Stop. Do NOT continue.**\n\n**If `openclaw` is missing:**\n\n> (CN) 你的系统没有安装 OpenClaw。请先安装 OpenClaw（>= 2026.5.27），然后再回来找我。\n> (EN) OpenClaw is not installed. Please install OpenClaw (>= 2026.5.27) first, then come back.\n\n**Stop. Do NOT continue.**\n\nIf both exist, proceed to STEP 3 silently.\n\n---\n\n## STEP 3: Greet and Ask for 3 Values\n\nSend this message:\n\n> (CN) 好，我来帮你接入 OpenViking 长期记忆。装好之后，我会自动记住对话里的重要信息，下次聊也能回忆起来。\n>\n> 我需要 3 条信息，不知道的可以问你的管理员：\n> 1. **OpenViking 服务地址** —— 例如 `https://ov.example.com` 或 `http://192.168.1.100:1933`，本机服务可以直接说\"本机\"\n> 2. **API Key** —— 用来鉴权；服务没开认证可以说\"没有\"\n> 3. **记忆归属**（可选） —— 所有对话共用 user 记忆（默认），还是在 `peers/<assistant_id>` 或 `peers/<sender_id>` 下分开存放\n>\n> 先告诉我服务地址吧？\n\n> (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.\n>\n> I need 3 things (ask your admin if unsure):\n> 1. **OpenViking server URL** — e.g. `https://ov.example.com` or `http://192.168.1.100:1933`. For a local server, just say \"local\".\n> 2. **API Key** — for auth. Say \"none\" if the server has no auth.\n> 3. **Memory scope** (optional) — shared user memory (default), or peer memory under `peers/<assistant_id>` / `peers/<sender_id>`.\n>\n> What's the server URL?\n\n---\n\n## STEP 4: Collect Values\n\nCollect 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.\n\n### 4a. `BASE_URL` (REQUIRED)\n\n- \"local\" / \"本机\" / \"localhost\" → use `http://127.0.0.1:1933`.\n- `ov.example.com` without protocol → prepend `https://`.\n- Strip trailing `/`, `/health`, or `/api`.\n- After normalization must start with `http://` or `https://`.\n- 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.**\n\n### 4b. `API_KEY` (OPTIONAL)\n\n> (CN) API Key 是什么？服务没开认证就直接说\"没有\"。\n> (EN) What's the API Key? Say \"none\" if the server has no auth.\n\n- \"no\" / \"none\" / \"没有\" / \"空\" / empty → `API_KEY=\"\"` (you will skip the flag later).\n- Otherwise store as-is.\n\n### 4c. `PEER_ROLE` (OPTIONAL)\n\n> (CN) 记忆要存在哪一层？默认共享在 `viking://user/<user_id>/memories`（`none`）。也可以按 OpenClaw 助手存到 `.../peers/<assistant_id>/memories`（`assistant`），或按发送者存到 `.../peers/<sender_id>/memories`（`sender`）。\n> (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`).\n\nDefault to `none`. **Never silently pick another value** — the three options mean different storage layouts and are awkward to change later:\n\n| Value | Concrete layout | Choose it when |\n|---|---|---|\n| `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. |\n| `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/...`. |\n| `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. |\n\n`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`.\n\nOpenViking 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.\n\n- Empty / \"default\" / \"默认\" / \"共用\" → leave unset (plugin defaults to `none`).\n- Otherwise pass `--peer-role none|assistant|sender`. Treat explicit legacy `person` as `sender`. Any other value → ask again.\n\n### 4d. `PEER_PREFIX` (OPTIONAL, only when `PEER_ROLE=assistant`)\n\n> (CN) 想给这个 agent 一个记忆前缀吗？留空就用默认。只能用字母、数字、`_`、`-`。\n> (EN) Want to set an peer prefix? Leave blank for the default. Letters, digits, `_`, `-` only.\n\n- Only meaningful when `PEER_ROLE=assistant`; skip the question otherwise.\n- Empty / \"default\" / \"默认\" → leave unset (plugin defaults to `\"\"`).\n- Otherwise validate against `/^[A-Za-z0-9_-]+$/`. If invalid, ask again.\n\n### 4e. (Conditional) Multi-Tenant Root-Key Fields\n\nOnly ask for these if STEP 7 detects a root key (`Root API key detected. Missing: --account-id, --user-id`). Don't ask up front.\n\n- `ACCOUNT_ID`\n- `USER_ID`\n\nSee **Reference: Multi-Tenant** for what these mean.\n\n---\n\n## STEP 5: Pre-flight Connectivity Check (silently)\n\nTell the user briefly:\n\n> (CN) 我先测一下能不能连上服务……\n> (EN) Let me test the connection to your server...\n\nRun:\n\n**If OS=windows:**\n\n```powershell\ntry { (Invoke-WebRequest -Uri \"BASE_URL/health\" -TimeoutSec 10 -UseBasicParsing -ErrorAction Stop).StatusCode } catch { $_.Exception.Response.StatusCode.value__ }\n```\n\n**If OS=mac or OS=linux:**\n\n```bash\ncurl -sS -o /dev/null -w \"%{http_code}\" --connect-timeout 10 \"BASE_URL/health\"\n```\n\nReplace `BASE_URL` with the actual value.\n\n| Status | Meaning | Action |\n|---|---|---|\n| `200` | Server reachable, no auth on `/health` | Proceed to STEP 6. |\n| `401` / `403` | Server reachable but `/health` requires auth | Proceed to STEP 6 — the wizard's key probe will sort it out. |\n| `000` / timeout / connection refused | Server unreachable | **Server-not-ready handling** below. |\n| Anything else | Unexpected | Show status code to the user, go back to STEP 4a. |\n\n### Server-not-ready handling\n\nThis skill **does not install or operate the OpenViking server**. If the user's server is unreachable, present the situation honestly and offer two paths:\n\n> (CN) ❌ 我连不上 `BASE_URL`。可能是：\n> 1) 服务还没启动 —— 请联系你的 OpenViking 服务管理员把它起起来；如果是你自己负责，请参考 OpenViking 官方文档（`https://github.com/volcengine/OpenViking`）的 server 启动指引。\n> 2) 地址不对 —— 你可以重新告诉我正确的地址。\n> 3) 网络不通（防火墙 / VPN / 内网）—— 你确认一下网络。\n>\n> 也可以选择\"先把配置写下来\"，等服务起来就自动生效，要这么办吗？\n\n> (EN) ❌ Cannot reach `BASE_URL`. Likely cause:\n> 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.**\n> 2) **Wrong URL** — give me the correct URL.\n> 3) **Network blocked** (firewall / VPN / private network) — please verify connectivity.\n>\n> Or I can save the config now (`--allow-offline`) so it will activate automatically once the server is up. Want me to do that?\n\nIf the user fixes the URL → back to STEP 4a.\nIf the user wants `--allow-offline` → remember `ALLOW_OFFLINE=true` and continue to STEP 6.\nIf the user gives up / cannot fix → stop here. Do NOT continue with a broken state.\n\n---\n\n## STEP 6: Install the Plugin\n\nThe 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.\n\n### Path A — Primary: `openclaw plugins install` (uses ClawHub)\n\nTell the user:\n\n> (CN) 现在开始装插件……\n> (EN) Installing the plugin now...\n\nRun:\n\n```bash\nopenclaw plugins install clawhub:@openviking/openclaw-plugin\n```\n\nTrigger fallback to Path B only if the output contains any of these strings:\n\n- `429`\n- `rate limit` / `rate-limited` / `Too Many Requests`\n- `not logged in` / `please log in` / `please login` / `unauthorized` / `401` / `403` together with `clawhub`\n- `ETIMEDOUT` / `ECONNRESET` on a `clawhub`-related host\n- generic message indicating the registry refused an anonymous client\n\nBefore falling back, also try the explicit registry prefix once:\n\n```bash\nopenclaw plugins install clawhub:@openviking/openclaw-plugin\n```\n\nIf the install **succeeds**, jump to STEP 7.\n\nIf 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.\n\n### Path B — Backup: `ov-install` (bypasses ClawHub)\n\nTell the user:\n\n> (CN) ClawHub 现在好像被限流、不可用，或者当前账号不能安装。我改用备用路径，通过 npm 下载并部署插件包。\n> (EN) ClawHub looks rate-limited, unavailable, or blocked for this account. I'll use the backup path and install the plugin package from npm.\n\nRun the installer with `npx` (no global install needed):\n\n```bash\nnpx -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]\n```\n\nBuild the flag list according to what the user gave you:\n\n- Always pass `--base-url BASE_URL`.\n- Pass `--api-key API_KEY` only if `API_KEY` is non-empty.\n- 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.\n- Pass `--peer-prefix PEER_PREFIX` only if the user gave one and `PEER_ROLE=assistant`.\n- `--account-id` / `--user-id` only if the root-key path requires them.\n\n`ov-install` will, in one shot:\n1. Download the `@openviking/openclaw-plugin` package from npm into a temporary staging dir.\n2. Copy the package into the OpenClaw `extensions/` dir and install plugin dependencies.\n3. Register the plugin in `openclaw.json` (via `openclaw plugins enable` or direct write).\n4. Run `openclaw openviking setup --json --base-url … [--api-key …]` for the user.\n5. Return a non-zero exit if setup needs explicit `--allow-offline` or `--force-slot` consent.\n\nThis 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).\n\nIf `ov-install` exits non-zero, capture the last 30 lines of its output, show them to the user, and stop. Don't retry blindly.\n\n---\n\n## STEP 7: Configure (only on Path A — Path B did this for you)\n\nRun the setup wizard non-interactively. Build flags from collected values:\n\n```bash\nopenclaw 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]\n```\n\nRules:\n\n- `--base-url BASE_URL` is **required** under `--json`. Without it, the wizard prints `--json requires --base-url for non-interactive mode`.\n- `--api-key` only if `API_KEY` is non-empty.\n- `--peer-role` only if the user chose `assistant` or `sender`; omit it for the `none` default. Normalize legacy `person` to `sender` first.\n- `--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.\n- `--account-id` / `--user-id` only after STEP 7 root-key detection (see below).\n- `--allow-offline` only if the user explicitly approved it in STEP 5.\n- `--force-slot` **never** in the first attempt. Add only after the user confirms (see slot_blocked handling below).\n\n### Parse the JSON output\n\nThe wizard prints a single JSON object:\n\n```json\n{\n  \"success\": true | false,\n  \"action\": \"configured\" | \"existing\" | \"error\" | \"slot_blocked\",\n  \"config\": { \"mode\": \"remote\", \"baseUrl\": \"...\", \"apiKey\": \"...\", \"peer_prefix\": \"...\", \"accountId\": \"...\", \"userId\": \"...\" },\n  \"health\": { \"ok\": true, \"version\": \"...\", \"compatibility\": \"compatible\" | \"server_too_old\" | \"server_too_new\" | \"unknown\" },\n  \"keyProbe\": { \"keyType\": \"user_key\" | \"root_key\" | \"no_key\" | \"unknown\", \"needsAccountId\": false, \"needsUserId\": false, \"detail\": \"...\" },\n  \"slot\": { \"activated\": true, \"replaced\": false, \"previousOwner\": \"openviking\" },\n  \"error\": \"...\"\n}\n```\n\n### Decision matrix\n\n| Condition | Action |\n|---|---|\n| `success: true` and `action: \"configured\"` or `\"existing\"` | Done. Proceed to STEP 9. |\n| `success: false` and `action: \"slot_blocked\"` | **Slot conflict — see below.** |\n| `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. |\n| `success: false` and `error` contains `\"Root API key detected\"` and `\"Missing: --account-id, --user-id\"` | **Root-key path — see below.** |\n| `success: false` and `error` contains `\"Invalid API key\"` / `keyProbe.keyType: \"unknown\"` after auth failure detail | API key wrong. Back to STEP 4b. |\n| `success: false` and any other `action: \"error\"` | Show `error` to the user, stop. Do NOT pretend success. |\n\n### Slot conflict (`slot_blocked`)\n\nThe error text looks like:\n\n> `contextEngine slot is owned by \"<other-plugin>\". Config was saved but slot was NOT changed. Use --force-slot to replace.`\n\n**Do NOT silently use `--force-slot`.** Ask the user:\n\n> (CN) 你的 OpenClaw 当前 contextEngine 槽被 `<other-plugin>` 占着。如果用 OpenViking 替换它，`<other-plugin>` 就不再生效。要替换吗？\n> (EN) Your `contextEngine` slot is currently owned by `<other-plugin>`. Activating OpenViking will disable it. Replace?\n\nIf 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.\n\n### Root-key path\n\nThe error text looks like:\n\n> `Root API key detected. Missing: --account-id, --user-id. Provide both to enable multi-tenant scoping.`\n\nAsk the user:\n\n> (CN) 你给的是 root 级 API Key，需要再补两个值才能用：账户 ID（accountId）和用户 ID（userId）。这两个一般是 OpenViking 服务管理员配的，不知道就问他们。\n> (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.\n\nAfter collecting, retry the setup command with `--account-id` and `--user-id` appended.\n\n---\n\n## STEP 8: (Reserved — done inside STEP 7 or by `ov-install`)\n\nThe 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.\n\n---\n\n## STEP 9: Restart the Gateway\n\n```bash\nopenclaw gateway restart\n```\n\nIf it fails, try once more with:\n\n```bash\nopenclaw gateway --force\n```\n\nIf both fail:\n\n> (CN) Gateway 没能自动重启。请你手动跑一下 `openclaw gateway restart`。重启完告诉我，我来验证。\n> (EN) Gateway didn't restart cleanly. Please run `openclaw gateway restart` manually, then tell me when it's done so I can verify.\n\nWait ~3 seconds before STEP 10.\n\n---\n\n## STEP 10: Verify\n\n```bash\nopenclaw openviking status --json\n```\n\nExpected output:\n\n```json\n{\n  \"configured\": true,\n  \"slotActive\": true,\n  \"health\": { \"ok\": true },\n  \"config\": { \"baseUrl\": \"...\", \"peer_prefix\": \"...\" }\n}\n```\n\n### Decision\n\n- `configured: true` **and** `slotActive: true` **and** `health.ok: true` → **success.** Send the success message below.\n- `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.\n- `configured: true` but `health.ok: false` → server became unreachable. Tell the user; the config will start working as soon as the server is back.\n- `configured: false` → setup didn't actually persist. Show full status JSON to the user and stop.\n\n### Success message\n\n> (CN) 🎉 全部搞定！OpenViking 长期记忆已经接好了。\n>\n> 工作方式：\n> - **每一轮**：你的对话会被自动追加到 OpenViking session 里；自动抽取依赖阈值 commit 或 `/compact`\n> - **明确要记住时**：如果你说“记住/保存/存一下”某个长期事实，我会用 `memory_store` 立即提交到记忆管线\n> - **`/compact` 时**：待提交的 session 消息会在 OpenViking 服务端被抽取成长期记忆\n> - **后续会话**：每次回复前我都会自动搜一下相关记忆并带进上下文\n>\n> 想验证一下吗？可以直接说“记住我的邮箱是 test@example.com”，让我通过 `memory_store` 立即提交；或者先聊几句包含可记忆事实的话，再调一下 `/compact`。之后开新对话问“我的邮箱是？”，应该就能召回了。\n\n> (EN) 🎉 All set! OpenViking long-term memory is connected.\n>\n> How it works:\n> - **Every turn**: our messages are appended to an OpenViking session; automatic extraction depends on a threshold commit or `/compact`\n> - **Explicit remember requests**: if you ask me to remember/save/store a long-term fact, I can use `memory_store` to commit it immediately\n> - **On `/compact`**: pending session messages are extracted into long-term memories on the OpenViking server\n> - **Future sessions**: relevant memories are auto-retrieved and injected before my replies\n>\n> 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.\n\n---\n\n# Part 2 — Reference\n\n## How It Works\n\nThe 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:\n\n- **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/`.\n- **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.\n  - `captureMode: \"semantic\"` (default): server extraction pipeline filters all qualifying text.\n  - `captureMode: \"keyword\"`: only text matching trigger words (e.g. \"remember\", \"preference\") is considered.\n- **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.\n\n**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.\n\n### Explicit long-term memory writes\n\nAuto-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.\n\nUse `memory_store` as the integration-side reliable path for durable-memory intent:\n\n- It writes the supplied text into an OpenViking session and calls `commit(wait=true)`.\n- It complements auto-capture; it does not replace normal session capture.\n- 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.\n\n## Available Tools\n\nThese are the plugin tools the agent can call once installed.\n\n### `memory_recall` — Search Memories\n\n| Parameter | Required | Description |\n|---|---|---|\n| `query` | Yes | Search query text |\n| `limit` | No | Maximum number of results (defaults to plugin config) |\n| `scoreThreshold` | No | Minimum relevance score 0–1 (defaults to plugin config) |\n| `targetUri` | No | Exact search scope URI. If provided, only that URI is searched. |\n| `resourceTypes` | No | Array of target types used when `targetUri` is omitted: `resource`, `user`, `agent`. Defaults to plugin `recallTargetTypes`. |\n\nExample: user asks \"What programming language did I say I like?\"\n\n### `memory_store` — Manual Store\n\n| Parameter | Required | Description |\n|---|---|---|\n| `text` | Yes | Information text to store |\n| `role` | No | Session role (default `user`) |\n| `sessionId` | No | Existing OpenViking session ID |\n\nUse this when the user explicitly asks to remember/save/store a long-term fact, preference, project, or decision.\n\nExample: user says \"Remember my email is xxx@example.com\".\n\n### `memory_forget` — Delete Memories\n\n| Parameter | Required | Description |\n|---|---|---|\n| `uri` | No | Exact memory URI (direct delete) |\n| `query` | No | Search query (find then delete) |\n| `targetUri` | No | Search scope URI |\n| `limit` | No | Search limit (default 5) |\n| `scoreThreshold` | No | Minimum relevance score |\n\nExample: user says \"Forget my phone number\".\n\n### `ov_archive_search` — Search Archived Original Messages\n\n| Parameter | Required | Description |\n|---|---|---|\n| `query` | Yes | A single keyword or short phrase. Prefer concrete names, dates, commands, paths, or distinctive nouns. |\n| `archiveId` | No | Optional archive ID such as `archive_002`. |\n\nUse when `[Session History Summary]` is too coarse. Try at least two keyword variants before concluding the detail is unavailable.\n\n### `ov_archive_expand` — Expand An Archive\n\n| Parameter | Required | Description |\n|---|---|---|\n| `archiveId` | Yes | Archive ID from `[Archive Index]`, e.g. `archive_002`. |\n\n### `add_resource` — Import Resource\n\nThe 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.\n\n| Parameter | Required | Description |\n|---|---|---|\n| `source` | Yes | Local path, OpenClaw media attachment path, directory path, public URL, or Git URL. |\n| `to` | No | Exact target URI under `viking://resources`; mutually exclusive with `parent`. |\n| `parent` | No | Parent URI under `viking://resources`; mutually exclusive with `to`. |\n| `reason` | No | Reason or note for import. |\n| `instruction` | No | Processing instruction for semantic extraction. |\n| `wait` | No | Wait for processing completion. |\n\n### `add_skill` — Import Agent Skill\n\n| Parameter | Required | Description |\n|---|---|---|\n| `source` | No | Local `SKILL.md` path or skill directory. Exactly one of `source` or `data` is required. |\n| `data` | No | Raw `SKILL.md` content or MCP tool dict. Exactly one of `source` or `data` is required. |\n| `wait` | No | Wait for processing completion. |\n\nAgent 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.\n\n### `ov_search` — Search Resources and Skills\n\n| Parameter | Required | Description |\n|---|---|---|\n| `query` | Yes | Search query. |\n| `uri` | No | Optional search URI. Defaults to resources plus agent skills. |\n| `limit` | No | Max results per search scope. Default: 10. |\n\nImportant: `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.\n\n### `ov_read` — Read OpenViking Content\n\n| Parameter | Required | Description |\n|---|---|---|\n| `uri` | Yes | Exact `viking://...` URI returned by `ov_search` or recall trace results. Local file paths and `openviking://...` display aliases are refused. |\n\n### `ov_recall_trace` — Query Recall Trace\n\n| Parameter | Required | Description |\n|---|---|---|\n| `turn` | No | `latest` or `all`; default `latest`. |\n| `traceId` | No | Exact trace ID. |\n| `sessionId` / `sessionKey` / `ovSessionId` | No | Session filters. |\n| `source` | No | `auto_recall`, `memory_recall`, `ov_search`, or `ov_archive_search`. |\n| `resourceTypes` | No | Target type filters: `resource`, `user`, `agent`. |\n| `since` / `until` | No | Unix timestamp bounds in milliseconds. |\n| `includeContent` | No | Read selected/displayed URI content previews on demand. |\n| `limit` | No | Maximum traces to return. Default: 20. |\n\nRequires `traceRecall=true`; persisted lookup also requires `traceRecallPersist=true`.\n\n### Externalized Tool Result Tools\n\nUse these when a preview contains a `viking://session/<session_id>/tool-results/<tool_result_id>` ref.\n\n| Tool | Parameters |\n|---|---|\n| `openviking_tool_result_list` | `tool_name?`, `limit?` (default 50) |\n| `openviking_tool_result_search` | `tool_output_ref`, `query`, `limit?` (default 20), `context_chars?` (default 300) |\n| `openviking_tool_result_read` | `tool_output_ref`, `offset?` (default 0), `limit?` (default 20000) |\n\n## Configuration Schema\n\nThese are the keys under `plugins.entries.openviking.config` in `openclaw.json`. The setup wizard / `ov-install` sets the first few; the rest are tunables.\n\n| Field | Default | Description |\n|---|---|---|\n| `mode` | `\"remote\"` (forced by plugin) | Always remote in this skill. Don't set manually. |\n| `baseUrl` | `http://127.0.0.1:1933` | OpenViking server URL. |\n| `apiKey` | — | API key. Optional if server has no auth. |\n| `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`. |\n| `peer_prefix` | `\"\"` | Optional prefix for assistant `peer_id` / actor peer values. Only used when `peer_role=assistant`. Letters / digits / `_` / `-`. |\n| `accountId` | — | Required when `apiKey` is a root key. |\n| `userId` | — | Required when `apiKey` is a root key. |\n| `targetUri` | `viking://~/memories` | Default search scope URI. |\n| `timeoutMs` | (plugin default) | HTTP timeout for OpenViking calls. |\n| `autoCapture` | `true` | Auto-append turn messages to the OpenViking session at `afterTurn`; extraction runs only after a threshold commit, `/compact`, or explicit `memory_store`. |\n| `captureMode` | `\"semantic\"` | Filter mode used by the server-side extraction pipeline: `semantic` or `keyword`. |\n| `captureMaxLength` | `24000` | Max text length per archived turn. |\n| `autoRecall` | `true` | Auto-recall and inject memories before reply. |\n| `recallTargetTypes` | `user,agent` | Default target types when `targetUri` is omitted. Allowed: `resource`, `user`, `agent`. |\n| `recallResources` | `false` | Compatibility shortcut that appends `resource` to default recall targets when `recallTargetTypes` is unset. |\n| `recallLimit` | `6` | Max memories injected per recall. |\n| `recallScoreThreshold` | `0.15` | Min relevance score to inject. |\n| `recallMaxInjectedChars` | `4000` | Hard cap on injected character count. Complete memories that do not fit are skipped, not truncated. |\n| `recallPreferAbstract` | (plugin default) | Prefer abstract memories over raw. |\n| `recallTokenBudget` | deprecated | Compatibility alias for `recallMaxInjectedChars`. |\n| `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. |\n| `commitKeepRecentCount` | `10` | Recent messages kept live after afterTurn commit. Compact always uses `0`. |\n| `bypassSessionPatterns` | — | Glob patterns for sessions skipped by capture. |\n| `ingestReplyAssist` | (plugin default) | Reply-assist ingestion toggle. |\n| `emitStandardDiagnostics` | `false` | Emit structured `openviking: diag {...}` lines. |\n| `logFindRequests` | `false` | Log routing for find/session writes. Also enabled by `OPENVIKING_LOG_ROUTING=1` or `OPENVIKING_DEBUG=1`. |\n| `traceRecall` | `false` | Record recall traces in memory. |\n| `traceRecallPersist` | `false` | Persist recall traces to JSONL files. |\n| `traceRecallDir` | `~/.openclaw/openviking/recall-traces` | Recall trace directory. |\n\nTo change a value:\n\n```bash\nopenclaw config set plugins.entries.openviking.config.<field> <value>\nopenclaw gateway restart\n```\n\n## Multi-Tenant (Root API Keys)\n\nSome 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:\n\n```\nRoot API key detected. Missing: --account-id, --user-id\n```\n\nWhen you see this:\n\n1. Ask the user for both values (they come from the OpenViking admin).\n2. Retry STEP 7 with both flags:\n\n```bash\nopenclaw openviking setup --base-url BASE_URL --api-key API_KEY --account-id ACCOUNT_ID --user-id USER_ID --json\n```\n\nA **user key** (issued per tenant) does not need these flags.\n\n## Multi-Instance (`--workdir` / `OPENCLAW_STATE_DIR`)\n\nIf the user runs multiple OpenClaw instances (e.g. testing several agents in parallel), each has its own state dir.\n\nTo target a non-default instance:\n\n```bash\nnpx -y openclaw-openviking-setup-helper@latest --workdir ~/.openclaw-second --base-url ... --api-key ...\n```\n\n`ov-install` writes a helper env file when the state dir is non-default:\n\n- Unix: `~/.openclaw/openviking.env` containing `export OPENCLAW_STATE_DIR='...'`\n- Windows: `~/.openclaw/openviking.env.bat` and `.ps1` setting the same variable\n\nSource it before running `openclaw` commands so they hit the correct state:\n\n**Unix:**\n```bash\nsource ~/.openclaw/openviking.env\nopenclaw status\n```\n\n**Windows (PowerShell):**\n```powershell\n. \"$HOME/.openclaw/openviking.env.ps1\"\nopenclaw status\n```\n\nOr pass `--workdir` directly to each `openclaw` invocation (note: not all `openclaw` subcommands honor `--workdir` consistently — when in doubt, prefer the env var).\n\n## Daily Operations\n\n```bash\n# Start or restart OpenClaw gateway after config changes\nopenclaw gateway restart\n\n# Check overall status\nopenclaw status\nopenclaw openviking status --json\n\n# Read current OpenViking slot\nopenclaw config get plugins.slots.contextEngine\n\n# Disable OpenViking memory (keep config, deactivate slot)\nopenclaw config set plugins.slots.contextEngine legacy\nopenclaw gateway restart\n\n# Re-enable\nopenclaw config set plugins.slots.contextEngine openviking\nopenclaw gateway restart\n```\n\n## Uninstall\n\n### Preferred: via OpenClaw plugin manager\n\n```bash\nopenclaw plugins uninstall openviking\nopenclaw config set plugins.slots.contextEngine legacy\nopenclaw gateway restart\n```\n\n### If installed through backup `ov-install`\n\n```bash\nnpx -y openclaw-openviking-setup-helper@latest --uninstall\n```\n\nThis will:\n\n1. Stop the OpenClaw gateway.\n2. Back up `openclaw.json`.\n3. Remove all OpenViking plugin config from `openclaw.json` (`plugins.entries.openviking`, `plugins.allow`, `plugins.installs.openviking`, `plugins.slots.contextEngine`).\n4. Move the plugin directory to `disabled-extensions/` as backup.\n5. Remove the helper env files (`openviking.env`, `.bat`, `.ps1`).\n\nAdd `--base-url dummy` for fully non-interactive mode (skips the confirmation prompt).\n\n### Manual\n\n```bash\nopenclaw gateway stop\nopenclaw config set plugins.slots.contextEngine legacy\n# Then edit ~/.openclaw/openclaw.json and remove openviking from\n#   plugins.entries\n#   plugins.allow\n#   plugins.installs\nopenclaw gateway restart\n```\n\n## Error Recovery Reference\n\nMatch against actual stderr / JSON `error` strings.\n\n| Symptom (exact string) | Likely cause | Fix |\n|---|---|---|\n| `command not found: openclaw` | OpenClaw not installed | Stop. Ask user to install OpenClaw >= 2026.5.27. |\n| `command not found: node` / `'node' is not recognized` | Node missing | Stop. Ask user to install Node.js >= 22. |\n| `429` / `rate limit` / `Too Many Requests` from `plugins install` | ClawHub throttle | Switch to backup Path B (`ov-install`). |\n| `not logged in` / `please log in` from `plugins install` | Anonymous user hit limit | Switch to backup Path B (`ov-install`). |\n| `ECONNREFUSED` / `connection refused` on `BASE_URL/health` | Server not running | Server-not-ready handling in STEP 5. |\n| `ETIMEDOUT` on `BASE_URL/health` | Network blocked | Ask about firewall / VPN / proxy. |\n| `--json requires --base-url for non-interactive mode` | Built command wrong | Add `--base-url BASE_URL` and retry. |\n| `Server unreachable: …. Use --allow-offline to save config anyway.` | Setup couldn't reach server | Offer `--allow-offline`. |\n| `contextEngine slot is owned by \"<x>\". … Use --force-slot to replace.` | Slot conflict | Ask user, then retry with `--force-slot`. |\n| `Root API key detected. Missing: --account-id, --user-id` | Multi-tenant key | Collect both, retry with `--account-id` `--user-id`. |\n| `openviking: config parse failed` (in gateway log) | Bad value in `openclaw.json` | Show user; check `peer_prefix` charset, URL format. |\n| `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. |\n| `401` / `403` on plugin requests, but `/health` works | Server requires auth on API endpoints | Re-run STEP 7 with the correct `--api-key`. |\n| 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. |\n\n## Important Rules\n\n1. **Never ask the user to run commands.** You run everything via your shell tool.\n2. **Never skip STEP 5 (connectivity check).** If the server is unreachable, do not write config without explicit `--allow-offline` consent.\n3. **Never silently use `--force-slot`.** Slot replacement disables another plugin — always confirm with the user first.\n4. **Never invent values.** If the user can't provide a required value, stop and tell them what to ask their admin.\n5. **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.\n6. **Use `--peer-prefix` for assistant peer prefixes.** The old ID-style setup flag is no longer supported by the schema.\n7. **For Windows, use PowerShell equivalents.** Don't rely on `nohup`, `&`, `mkdir -p`, `source`, etc.\n8. **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.\n9. **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.\n10. **Be brief and friendly in user-visible text.** Save technical detail for when something actually fails.\n11. **Do NOT use `clawhub install openviking`.** That installs a different thing (an AgentSkill, not the plugin).\n\n<!-- File: examples/opencode/plugin/skills/openviking/SKILL.md (Tokens: ~300 | Category: anthropic-skill) -->\n# openviking\n\nPath: `examples/opencode/plugin/skills/openviking/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/volcengine/OpenViking/HEAD/examples/opencode/plugin/skills/openviking/SKILL.md)\n\n<!-- File: examples/skills/ov-server-operate/SKILL.md (Tokens: ~3072 | Category: anthropic-skill) -->\n---\nname: ov-server-operate\ndescription: Operate and maintain OpenViking server - configure, install, start, stop, and cleanup the server. Use when need to setup or manage OpenViking service deployment.\ncompatibility: OpenViking CLI configured at `~/.openviking/ovcli.conf`\n---\n\n# OpenViking Server Operations\n\nThis guide provides standard operating procedures for deploying, managing, and maintaining OpenViking servers in production environments.\n\n## Table of Content\n- Service Configuration\n- Environment Setup with uv\n- Server Startup with nohup\n- Server Shutdown\n- Data Cleanup Procedure\n- Verification and Troubleshooting\n\n## Service Configuration\n\n### Default Paths and Structure\n\nOpenViking uses the following standard directory structure under `~/.openviking/`:\n\n```\n~/.openviking/\n├── ov.conf             # Server configuration (required)\n├── ovcli.conf          # CLI client configuration\n├── ov-venv/            # Virtual environment (created by uv)\n├── log/                # Server log directory\n│   ├── openviking-server.log   # server stdout log\n│   └── openviking.log          # server log\n└── data/               # Workspace data (configured in ov.conf)\n    ├── ...\n    └── ...\n```\n\n### Configuration Files\n\n#### 1. Server Config (`~/.openviking/ov.conf`)\n\nCreate the configuration file with at minimum the following configuration.\nNote 1: Replace the api-key with your own api-key. If you don't have one, ask the user to get one (follow the Volcengine Ark platform guide).\nNote 2: Replace the root_api_key with your own root-api-key. Ask the user to set one — it will be used for authentication when the CLI connects to the server.\n\n```json\n{\n  \"server\": {\n    \"host\": \"0.0.0.0\",\n    \"port\": 1933,\n    \"root_api_key\": \"your-root-api-key\"\n  },\n  \"storage\": {\n    \"workspace\": \"~/.openviking/data/\"\n  },\n  \"parsers\": {\n    \"code\": {\n      \"gitlab_domains\": [\"code.byted.org\"],\n      \"azure_devops_domains\": [\"ssh.dev.azure.com\", \"vs-ssh.visualstudio.com\"]\n    }\n  },\n  \"embedding\": {\n    \"dense\": {\n        \"model\": \"doubao-embedding-vision-251215\",\n        \"api_key\": \"your-volcengine-api-key\",\n        \"api_base\": \"https://ark.cn-beijing.volces.com/api/v3\",\n        \"dimension\": 1024,\n        \"input\": \"multimodal\",\n        \"provider\": \"volcengine\"\n    }\n  },\n  \"vlm\": {\n    \"model\": \"doubao-seed-1-8-251228\",\n    \"api_key\": \"your-volcengine-api-key\",\n    \"api_base\": \"https://ark.cn-beijing.volces.com/api/v3\",\n    \"temperature\": 0.0,\n    \"max_retries\": 2,\n    \"provider\": \"volcengine\",\n    \"thinking\": false\n  },\n  \"log\": {\n    \"level\": \"INFO\",\n    \"output\": \"file\",\n    \"rotation\": true,\n    \"rotation_days\": 3,\n    \"rotation_interval\": \"midnight\"\n  }\n}\n```\n\n#### 2. CLI Config (`~/.openviking/ovcli.conf`)\n\nFor client connections from localhost:\n\n```json\n{\n  \"url\": \"http://localhost:1933\",\n  \"api_key\": \"your-root-api-key\"\n}\n```\n\nFor remote connections, set the url to the remote server address (for example, the server EIP).\n\n## Environment Setup with uv\n\n### Step 1: Install uv (if not already installed)\n\n```bash\n# macOS/Linux\ncurl -LsSf https://astral.sh/uv/install.sh | sh\n\n# Verify installation\nuv --version\n```\n\n### Step 2: Create Virtual Environment\n\nCreate a dedicated virtual environment at `~/.openviking/ov-venv`:\n\n```bash\n# Create venv with Python 3.10+\ncd ~/.openviking\nuv venv --python 3.12 ov-venv\n```\n\n### Step 3: Activate and Install OpenViking\n\n```bash\n# Activate the virtual environment\nsource ~/.openviking/ov-venv/bin/activate\n\n# Install or upgrade to latest openviking\nuv pip install --upgrade openviking --force-reinstall\n\n# Verify installation\nwhich openviking-server\nopenviking-server --version\nopenviking-server --help\n```\n\n### Step 4: Create Log Directory\n\n```bash\nmkdir -p ~/.openviking/log\n```\n\n## Server Startup with nohup\n\n### Standard Startup Procedure\n\n```bash\n# 1. Activate the virtual environment\nsource ~/.openviking/ov-venv/bin/activate\n\n# 2. Ensure log directory exists\nmkdir -p ~/.openviking/log\n\n# 3. Start server with nohup\nnohup openviking-server \\\n    > ~/.openviking/log/openviking-server.log 2>&1 &\n\n# 4. Save PID for later reference\necho $! > ~/.openviking/server.pid\n\n# 5. Verify startup after 10 secs\nsleep 10\ncurl -s http://localhost:1933/health\n```\n\n### Verify Server is Running\n\n```bash\n# Method 1: Check health endpoint\ncurl http://localhost:1933/health\n# Expected: {\"status\": \"ok\"}\n\n# Method 2: Check readiness (includes storage checks)\ncurl http://localhost:1933/ready\n\n# Method 3: Check process\nps aux | grep openviking-server | grep -v grep\n\n# Method 4: Check log output\ntail -10 ~/.openviking/log/openviking-server.log\ntail -50 ~/.openviking/log/openviking.log\n```\n\n## Server Shutdown\n\n### Graceful Shutdown Procedure\n\n```bash\n# 1. Find the server process\nps aux | grep openviking-server | grep -v grep\n\n# 2. Send SIGTERM for graceful shutdown\n# Option A: Using saved PID\nif [ -f ~/.openviking/server.pid ]; then\n    kill $(cat ~/.openviking/server.pid)\n    rm ~/.openviking/server.pid\nfi\n\n# Option B: Using pgrep\npkill -f openviking-server\n\n# 3. Wait for process to stop\nsleep 3\n\n# 4. Verify it stopped\nps aux | grep openviking-server | grep -v grep || echo \"Server stopped successfully\"\n\n# 5. If still running, force kill\nif pgrep -f openviking-server > /dev/null; then\n    echo \"Force killing server...\"\n    pkill -9 -f openviking-server\nfi\n```\n\n## Data Cleanup Procedure\n\n### When to Use This Procedure\n\nPerform full data cleanup in these scenarios:\n1. Version upgrade with incompatible data format\n2. Corrupted or inconsistent data\n3. Need to reset to fresh state\n4. Storage space reclamation\n\n### Standard Cleanup Workflow\n\n**CRITICAL: ALWAYS BACKUP BEFORE DELETING DATA**\n\n```bash\n# ==========================================\n# STEP 1: STOP THE SERVER FIRST\n# ==========================================\necho \"Step 1: Stopping OpenViking Server...\"\nif pgrep -f openviking-server > /dev/null; then\n    pkill -f openviking-server\n    sleep 3\n    if pgrep -f openviking-server > /dev/null; then\n        pkill -9 -f openviking-server\n        sleep 1\n    fi\nfi\n\n# Verify server is stopped\nif pgrep -f openviking-server > /dev/null; then\n    echo \"ERROR: Server still running! Cannot proceed.\"\n    exit 1\nfi\necho \"✓ Server stopped\"\n\n# ==========================================\n# STEP 2: CREATE BACKUP (REQUIRED)\n# ==========================================\necho \"\"\necho \"Step 2: Creating backup...\"\nBACKUP_DATE=$(date +%Y%m%d_%H%M%S)\nBACKUP_DIR=~/.openviking/backup_${BACKUP_DATE}\n\nmkdir -p ${BACKUP_DIR}\n\n# Backup config files\ncp ~/.openviking/ov.conf ${BACKUP_DIR}/ 2>/dev/null || true\ncp ~/.openviking/ovcli.conf ${BACKUP_DIR}/ 2>/dev/null || true\n\n# Backup workspace (if exists)\nWORKSPACE=$(python3 -c '\nimport json\nimport os\nconfig_path = os.path.expanduser(\"~/.openviking/ov.conf\")\nif os.path.exists(config_path):\n    with open(config_path) as f:\n        cfg = json.load(f)\n        ws = cfg.get(\"storage\", {}).get(\"workspace\", \"./data\")\n        print(os.path.expanduser(ws))\n' 2>/dev/null || echo \"~/.openviking/data\")\n\nif [ -d \"${WORKSPACE}\" ]; then\n    echo \"Backing up workspace: ${WORKSPACE}\"\n    tar -czf ${BACKUP_DIR}/workspace_backup.tar.gz -C $(dirname ${WORKSPACE}) $(basename ${WORKSPACE})\nfi\n\n# Backup log\nif [ -d ~/.openviking/log ]; then\n    cp -r ~/.openviking/log ${BACKUP_DIR}/ 2>/dev/null || true\nfi\n\necho \"✓ Backup created at: ${BACKUP_DIR}\"\nls -lh ${BACKUP_DIR}/\n\n# ==========================================\n# STEP 3: CONFIRM DELETION\n# ==========================================\necho \"\"\necho \"==========================================\"\necho \"WARNING: ABOUT TO DELETE ALL DATA!\"\necho \"==========================================\"\necho \"Workspace to delete: ${WORKSPACE}\"\necho \"Backup location: ${BACKUP_DIR}\"\necho \"\"\nread -p \"Type 'DELETE' to confirm data removal: \" CONFIRM\n\nif [ \"${CONFIRM}\" != \"DELETE\" ]; then\n    echo \"Cleanup cancelled. Backup preserved at ${BACKUP_DIR}\"\n    exit 0\nfi\n\n# ==========================================\n# STEP 4: DELETE DATA\n# ==========================================\necho \"\"\necho \"Step 4: Deleting data...\"\n\n# Delete workspace\nif [ -d \"${WORKSPACE}\" ]; then\n    echo \"Deleting workspace: ${WORKSPACE}\"\n    rm -rf \"${WORKSPACE}\"\nfi\n\n# Optional: Delete old log (uncomment if needed)\n# echo \"Deleting old log...\"\n# rm -rf ~/.openviking/log/*\n\n# Cleanup any temporary files\nrm -f ~/.openviking/server.pid\n\necho \"✓ Data deleted successfully\"\n\n# ==========================================\n# STEP 5: COMPLETION\n# ==========================================\necho \"\"\necho \"==========================================\"\necho \"Cleanup Complete!\"\necho \"==========================================\"\necho \"Backup preserved at: ${BACKUP_DIR}\"\necho \"\"\necho \"Next steps:\"\necho \"1. Reconfigure ov.conf if needed\"\necho \"2. Start the server: openviking-server\"\necho \"3. Verify with: curl http://localhost:1933/health\"\necho \"\"\necho \"To restore from backup:\"\necho \"  tar -xzf ${BACKUP_DIR}/workspace_backup.tar.gz -C $(dirname ${WORKSPACE})\"\n```\n\n### Quick Cleanup (for Development Only)\n\n```bash\n# WARNING: Only use in development!\n# No backup created - data loss guaranteed!\n\n# 1. Stop server\npkill -f openviking-server\nsleep 2\npkill -9 -f openviking-server 2>/dev/null || true\n\n# 2. Delete workspace (adjust path as needed)\nrm -rf ~/.openviking/data\n\n# 3. Cleanup PID and temp files\nrm -f ~/.openviking/server.pid\n\necho \"Quick cleanup complete\"\n```\n\n## Verification and Troubleshooting\n\n### Health Check Verification\n\n```bash\n# Basic health check (always available)\ncurl http://localhost:1933/health\n# Expected: {\"status\": \"ok\"}\n\n# Readiness check (verifies all components)\ncurl http://localhost:1933/ready\n# Expected: {\"status\": \"ready\", \"checks\": {\"agfs\": \"ok\", \"vectordb\": \"ok\", \"api_key_manager\": \"ok\"}}\n\n# System status via CLI (~/.openviking/ovcli.conf should be configured)\nov status\n```\n\n### Common Issues and Solutions\n\n#### Issue: Server won't start\n\n**Check:**\n```bash\n# 1. Check if port is in use\nlsof -i :1933\nnetstat -tulpn | grep 1933\n\n# 2. Check log for errors\ntail -10 ~/.openviking/log/openviking-server.log\ntail -100 ~/.openviking/log/openviking.log\n\n\n# 3. Verify config file is valid JSON\npython3 -c 'import json, os; json.load(open(os.path.expanduser(\"~/.openviking/ov.conf\"))); print(\"Config is valid\")'\n\n# 4. Verify virtual environment\nsource ~/.openviking/ov-venv/bin/activate\nwhich openviking-server\npip list | grep openviking\n```\n\n**Solution:**\n```bash\n# If port conflict: kill the process or use different port\nlsof -ti :1933 | xargs kill -9 2>/dev/null || true\n\n# Or start on different port\nnohup openviking-server --port 1934 > ~/.openviking/log/openviking-server.log 2>&1 &\n```\n\n#### Issue: API Key Errors\n\n**Check:**\n```bash\n# Verify API keys in config\npython3 -c '\nimport json, os\ncfg = json.load(open(os.path.expanduser(\"~/.openviking/ov.conf\")))\nprint(\"Embedding provider:\", cfg.get(\"embedding\", {}).get(\"dense\", {}).get(\"provider\"))\nprint(\"VLM provider:\", cfg.get(\"vlm\", {}).get(\"provider\"))\nprint(\"API keys set:\", bool(cfg.get(\"embedding\", {}).get(\"dense\", {}).get(\"api_key\")), bool(cfg.get(\"vlm\", {}).get(\"api_key\")))\n'\n```\n\n**Solution:** Verify API keys are correct and have the required permissions. Check network connectivity to the model provider endpoints. Ensure API keys are not expired.\n\n## Prerequisites\n\n- Python 3.10+ installed\n- uv package manager available\n- Sufficient disk space for workspace and log\n- API keys for embedding and VLM models configured\n- Network access to model providers (if using cloud models)\n\n<!-- File: examples/claude-code-memory-plugin/.claude-plugin/plugin.json (Tokens: ~300 | Category: plugin-manifest) -->\n# claude-plugin Plugin Manifest\n\nPath: `examples/claude-code-memory-plugin/.claude-plugin/plugin.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/volcengine/OpenViking/HEAD/examples/claude-code-memory-plugin/.claude-plugin/plugin.json)\n\n<!-- File: examples/codex-memory-plugin/.codex-plugin/plugin.json (Tokens: ~300 | Category: plugin-manifest) -->\n# codex-plugin Plugin Manifest\n\nPath: `examples/codex-memory-plugin/.codex-plugin/plugin.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/volcengine/OpenViking/HEAD/examples/codex-memory-plugin/.codex-plugin/plugin.json)\n\n<!-- File: examples/openclaw-plugin/openclaw.plugin.json (Tokens: ~300 | Category: plugin-manifest) -->\n# openclaw-plugin Plugin Manifest\n\nPath: `examples/openclaw-plugin/openclaw.plugin.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/volcengine/OpenViking/HEAD/examples/openclaw-plugin/openclaw.plugin.json)\n</yakaai_skills>"}