Files
OpenViking/examples/opencode-plugin/INSTALL-ZH.md
T
t0saki 6fb370cfba fix(memory-plugin): run shell commands that carry viking:// and attach a notice (#5131)
* fix(memory-plugin): split the viking:// URI guard into deny and notice

A shell command that carries a viking:// URI is not necessarily trying to
open it: ov CLI arguments, HTTP payloads and grep patterns all mention one.
The guard used to deny every such command, and models learned to split the
URI to get past it.

evaluateUriGuard now denies only file tools whose path is a viking:// URI.
evaluateUriNotice returns a notice for shell tools instead, naming the
plugin, the replacement tool and telling the model to ignore it when the URI
is intentional. preToolUseOutput wraps both for the PreToolUse hosts.

* fix(memory-plugin): stop treating grep's pattern as a path

pattern was one of the path keys, so Grep(pattern="viking://", path="/repo")
was denied on every host that guards grep, although it only searches local
files for the text. It stays a location for glob, which the generic sweep
still reaches.

* fix(dsh): run shell commands that carry viking:// and attach a notice

bash is no longer denied by tools/pre-execute. A tools/post-execute listener
delegates to the rest of the chain first, then appends a plugin context with
form "notice" when the command carried a viking:// URI, so a later listener's
block or content replacement survives. pluginMessage moves to capture.mjs and
takes the whole source, since a notice needs a summary as well as a form.

* fix(pi): notice on tool_result instead of blocking bash

tool_call now denies only read/grep/find/ls on a viking:// path. A bash
command that carries a URI runs, and tool_result appends the notice after the
result's own content blocks. The shared plugin-config test reads pi's version
from its package.json, like the other harnesses, instead of a literal.

* fix(agent-hook-plugin): trae notices shell commands, cursor stops guarding the shell

TRAE and ZCode use the shared preToolUseOutput, so Bash and RunCommand on
TRAE get additionalContext instead of a deny. ZCode's matcher still names no
shell tool, because its strict output schema is not verified to accept that
envelope.

Cursor has no channel that shows the model a note after a shell command, so
beforeShellExecution is dropped. The installer prunes an entry an older
install left behind, and the guard ignores a shell event that still arrives.

* feat(opencode): notice on tool.execute.after

bash had no guard on opencode. A command that carries a viking:// URI now
gets the notice appended to its output; read/glob/grep keep their deny in
tool.execute.before.

* feat(claude-code): guard Edit/Write and notice on Bash

The PreToolUse matcher grows from Read|Glob|Grep to
Read|Glob|Grep|Edit|Write|Bash. Edit and Write on a viking:// path are denied
like the read tools, and a Bash command that carries one gets
additionalContext. The script is now just preToolUseOutput, so the shared
library drops the guarded option that only this script used.

* feat(codex): add the PreToolUse URI guard

Codex gets the same uri-guard script as claude-code on a Bash matcher. Its
Edit and Write matchers are aliases for apply_patch, whose input is a patch
body with no path to deny, so the hook only ever adds a notice. The doctor
expects the sixth hook trust record, and users approve it once in /hooks.

* docs: capability reference rows for the deny/notice guard
2026-09-17 17:25:20 +08:00

9.9 KiB
Raw Permalink Blame History

安装 OpenViking OpenCode 统一插件

这个插件新增了一个面向 OpenCode 的统一 OpenViking 插件:

  • 面向 memory、resources 和 code context 的 OpenViking MCP 工具
  • 长期记忆、session 同步、生命周期边界 commit、自动 recall

这是仓库中唯一继续维护的 OpenCode 插件示例。这个插件不再安装 skills/openviking/SKILL.md,也不要求 agent 使用 ov 命令。模型工具由 Claude Code 和 Codex 记忆插件同款的 stdio MCP proxy 提供。

前置条件

需要先准备:

  • OpenCode
  • OpenViking HTTP Server
  • Node.js 18+
  • 如果服务端启用了认证,需要可用的 OpenViking API Key

建议先启动 OpenViking:

openviking-server --config ~/.openviking/ov.conf

检查服务:

curl http://localhost:1933/health

安装方式一:发布包安装

普通用户推荐通过 OpenCode 的 package plugin 机制启用:

{
  "plugin": ["@openviking/opencode-plugin"]
}

安装方式二:源码安装

用于开发调试或 PR 测试。OpenCode 推荐插件目录:

~/.config/opencode/plugins

在仓库根目录执行:

node examples/memory-plugin-shared/sync.mjs
mkdir -p ~/.config/opencode/plugins/openviking
cp examples/opencode-plugin/wrappers/openviking.js ~/.config/opencode/plugins/openviking.js
cp examples/opencode-plugin/index.mjs examples/opencode-plugin/package.json ~/.config/opencode/plugins/openviking/
cp -r examples/opencode-plugin/lib ~/.config/opencode/plugins/openviking/
cp -r examples/opencode-plugin/servers ~/.config/opencode/plugins/openviking/

sync.mjs 会生成 lib/shared/,插件和 MCP 代理都从这里 import 共享模块。这个目录不在 git 里,所以复制前要先运行;之后每次 git pull 也要重新运行再复制。

安装后结构应类似:

~/.config/opencode/plugins/
├── openviking.js
└── openviking/
    ├── index.mjs
    ├── package.json
    ├── lib/
    └── servers/

顶层 openviking.js 只负责把 OpenCode 能发现的一级 .js 入口转发到插件目录:

export { OpenVikingPlugin, default } from "./openviking/index.mjs"

这个 wrapper 只用于上面这种源码安装目录结构。npm 包安装会通过 package.json 直接加载 index.mjs。 源码安装请使用 .js wrapper;OpenCode 的本地插件扫描器会发现 JavaScript/TypeScript 插件文件。

如果你使用 npm 包方式安装,也可以将 examples/opencode-plugin 作为一个普通 OpenCode 插件包使用。

配置

行为旋钮写在共享的客户端配置文件里:

~/.openviking/ovcli.conf

示例配置:

{
  "url": "http://127.0.0.1:1933",
  "api_key": "your-api-key-here",
  "plugin": {
    "recallLimit": 6,
    "opencode": {
      "enabled": true,
      "mcpEnabled": true,
      "timeoutMs": 30000,
      "repoContext": true,
      "repoContextCacheTtlMs": 60000,
      "autoRecall": true,
      "scoreThreshold": 0.35,
      "recallMaxContentChars": 500,
      "recallPreferAbstract": true,
      "recallTokenBudget": 2000,
      "minQueryLength": 3,
      "commitTokenThreshold": 20000,
      "commitKeepRecentCount": 10,
      "profileTokenBudget": 10000,
      "resumeContextBudget": 32000
    }
  }
}

plugin 里的键对所有 harness 生效;plugin.opencode 里的键只对本插件生效,并覆盖前者。解析顺序是 OPENVIKING_* 环境变量 → 工作区的 .openviking/config.json、.openviking/config.local.json 和本机 registry 条目 → plugin.opencode → plugin → 内置默认值。每个旋钮的类型、默认值、取值范围、环境变量名和兼容的旧拼写都声明在 examples/memory-plugin-shared/lib/config-schema.mjs。

recallLimit 是遗留的配额缩放输入,不是最终结果上限。显式设置为 1 到 5 时,有效总配额仍为 6,因为六个 coding 分类会各保留一个检索槽位。

推荐通过环境变量提供 API Key,而不是写入配置文件:

export OPENVIKING_API_KEY="your-api-key-here"

API key 会从环境变量或 ~/.openviking/ovcli.conf 读取,并由 hooks 和 MCP proxy 作为 Authorization: Bearer ... 发送。account 和 user 是 trusted mode 身份头,会作为 X-OpenViking-Account、X-OpenViking-User 发送;api_key 模式的服务端从 key 里取身份,插件在那里不发这两个头。 peerId 会作为 X-OpenViking-Actor-Peer 用于数据面的 memory/resource 请求;捕获 session message 时仍写入 body peer_id。需要 peer 维度路由时请显式配置。

OPENVIKING_API_KEY、OPENVIKING_ACCOUNT、OPENVIKING_USER、 OPENVIKING_PEER_ID 优先级高于 ovcli.conf 里的同名配置。

高级场景可以用 OPENVIKING_CLI_CONFIG_FILE 指向其他路径的 ovcli.conf。

仅 Hooks 模式

如果其他 MCP server 已经提供 OpenViking,可以关闭本插件附带的 MCP 注册,同时保留生命周期 hooks:

{
  "plugin": {
    "opencode": { "mcpEnabled": false }
  }
}

repository context、自动 recall、消息 capture 和生命周期 commit 会继续工作,也不会添加或覆盖 OpenCode 的 mcp.openviking 配置。

验证

修改插件或 OpenViking 配置后,需要重启 OpenCode。

进入新的 OpenCode session 后,可以让 agent 浏览 OpenViking memory,或搜索一个已索引的资源。插件应暴露 OpenViking MCP server,OpenCode 中的工具名会带 openviking_ 前缀:

  • openviking_search、openviking_find
  • openviking_read、openviking_list、openviking_tree、openviking_grep、openviking_glob
  • openviking_remember、openviking_write、openviking_edit、openviking_add_resource
  • openviking_list_watches、openviking_cancel_watch、openviking_forget、openviking_health
  • openviking_list_watches、openviking_cancel_watch

如果行为异常,先查看运行时文件:

ls ~/.config/opencode/openviking/
tail -n 100 ~/.config/opencode/openviking/openviking-memory.log

如果使用本地 server,也确认 OpenViking 可访问:

curl http://localhost:1933/health

可用 MCP 工具

插件会通过 OpenCode config 注册 OpenViking stdio MCP proxy。服务端实际返回的 tools/list 是最终工具清单;当前 OpenViking server 暴露:

  • openviking_search:跨 memories/resources/skills 的深度语义检索;使用 mode="context" 获取面向当前任务、可直接注入的平衡上下文
  • openviking_find:快速语义检索
  • openviking_remember:存储重要事实或决策,供记忆提取
  • openviking_read:读取一个或多个 viking:// 文件
  • openviking_list:列出 viking:// 目录
  • openviking_tree:展示 viking:// 目录树
  • openviking_grep:精确文本或正则搜索
  • openviking_glob:glob 文件匹配
  • openviking_write:创建、覆盖或追加 viking:// 文件
  • openviking_edit:对 viking:// 文件做精确字符串替换
  • openviking_add_resource:添加 URL、本地文件、sitemap 或 feed
  • openviking_forget:在用户明确确认后删除 viking:// URI
  • openviking_list_watches / openviking_cancel_watch:查看或取消资源 watch
  • openviking_health:检查 OpenViking server 健康状态

使用建议:

  • 概念性问题用 openviking_search
  • 精确符号、函数名、类名、报错字符串用 openviking_grep
  • 枚举文件用 openviking_glob
  • 读取内容用 openviking_read
  • 探索目录结构用 openviking_list
  • 删除前必须先获得用户明确确认,再调用 openviking_forget
  • 如果 agent 误用 OpenCode 本地 read、glob、grep 工具访问 viking:// URI,插件会阻止这次本地文件系统调用,并提示改用 MCP 工具。
  • bash 命令里带 viking:// URI 时照常执行,插件会在输出末尾附一段提示,建议改用 MCP 工具;URI 本来就是命令参数时,agent 可以忽略这段提示。

openviking_add_resource 本地文件

openviking_add_resource 支持三类输入:

  • 远端 http(s) URL:直接调用 /api/v1/resources
  • 本地文件路径:先调用 /api/v1/resources/temp_upload,再用返回的 temp_file_id 添加资源
  • file:// URL:按本地文件处理

相对路径会按 OpenCode 当前项目目录解析。示例:

openviking_add_resource(path="https://example.com/spec.md", to="viking://resources/spec")
openviking_add_resource(path="./docs/notes.md", to="viking://resources/notes.md")
openviking_add_resource(path="file:///home/alice/project/notes.md", description="project notes")

当前仍不支持本地目录自动打 zip 上传;传入目录时会返回明确错误。

运行时文件

插件默认会把运行时文件写入:

~/.config/opencode/openviking/

可能包含:

  • openviking-memory.log
  • openviking-session-state.json

可以通过 plugin.opencode 里的 dataDir 修改这个目录。

这些是本地运行时文件,不建议提交到版本库。

故障排查

问题 排查方向
插件没有加载 package 安装检查 ~/.config/opencode/opencode.json 是否包含 @openviking/opencode-plugin;源码安装检查 ~/.config/opencode/plugins/openviking.js 是否存在
加载时报找不到 lib/shared/*.mjs 源码复制前没有运行 sync.mjs。在仓库根目录运行 node examples/memory-plugin-shared/sync.mjs 后重新复制 lib/
MCP tools 连到了错误的 server 检查 ~/.openviking/ovcli.conf,或用 OPENVIKING_* 环境变量 / OPENVIKING_CLI_CONFIG_FILE 指向正确配置
OpenViking 返回 401 / 403 检查 OPENVIKING_API_KEY;trusted-mode 部署还要检查 OPENVIKING_ACCOUNT 和 OPENVIKING_USER
recall 为空 确认 OpenViking 中已有 memories/resources,并且 autoRecall 为 true
本地 openviking_add_resource 失败 传入文件路径而不是目录;目前还不支持自动上传本地目录