10 KiB
OpenViking OpenCode Plugin
A unified OpenCode plugin for OpenViking repository retrieval and long-term memory.
Requires an OpenViking server with
viking://~home-alias support. Recall targets the caller's own context space throughviking://~/memoriesandviking://~/skills; the uid-lessviking://user/memoriesshorthand is rejected by newer servers.
This is the only OpenCode plugin example maintained in this repository. It supersedes the former split examples for indexed repository prompt injection and long-term memory.
The plugin uses OpenCode hooks for lifecycle behavior and registers OpenViking's standard stdio MCP proxy for model tools. It does not install or require an OpenCode skill, and agents do not need to run ov shell commands.
What It Does
- Injects indexed
viking://resources/repositories into the system prompt. - Exposes the same OpenViking MCP tools used by the Claude Code and Codex memory plugins.
- Maps each OpenCode session to an OpenViking session.
- Captures user and assistant text messages into OpenViking.
- Commits sessions at lifecycle boundaries for memory extraction.
- Automatically recalls relevant memories and injects them as hidden synthetic context for the current user message.
- Blocks accidental local filesystem reads of
viking://URIs and points the agent back toopenviking_read,openviking_glob, oropenviking_search.
Files
examples/opencode-plugin/
├── index.mjs
├── package.json
├── README.md
├── INSTALL-ZH.md
├── lib/
│ ├── config.mjs
│ ├── mcp-config.mjs
│ ├── runtime.mjs
│ ├── repo-context.mjs
│ ├── memory-session.mjs
│ ├── memory-recall.mjs
│ ├── session-inject.mjs
│ ├── viking-uri-guard.mjs
│ └── utils.mjs
├── servers/
│ └── mcp-proxy.mjs
├── tests/
└── wrappers/
└── openviking.js
There is intentionally no skills/openviking/SKILL.md. The tool surface comes from OpenViking's MCP endpoint.
Requirements
- OpenCode
- OpenViking HTTP server
- Node.js 18+
- An OpenViking API key if your server requires authentication
Start OpenViking first:
openviking-server --config ~/.openviking/ov.conf
Installation
Published Package
Normal users should enable it through OpenCode's package plugin mechanism:
The published npm package is @openviking/opencode-plugin; verify availability with:
npm view @openviking/opencode-plugin version
{
"plugin": ["@openviking/opencode-plugin"]
}
Source Install
For development or PR testing, copy the package into OpenCode's plugin directory with a top-level wrapper:
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/
This creates a stable OpenCode plugin layout:
~/.config/opencode/plugins/
├── openviking.js
└── openviking/
├── index.mjs
├── package.json
├── lib/
└── servers/
The top-level openviking.js is only a wrapper:
export { OpenVikingPlugin, default } from "./openviking/index.mjs"
This wrapper is only for source installs with the directory layout shown above. npm package installs load index.mjs directly through package.json.
Use the .js wrapper for source installs; OpenCode's local plugin scanner discovers JavaScript/TypeScript plugin files.
Configuration
Create ~/.config/opencode/openviking-config.json:
{
"enabled": true,
"mcp": { "enabled": true },
"timeoutMs": 30000,
"repoContext": { "enabled": true, "cacheTtlMs": 60000 },
"autoRecall": {
"enabled": true,
"limit": 6,
"scoreThreshold": 0.35,
"maxContentChars": 500,
"preferAbstract": true,
"tokenBudget": 2000,
"minQueryLength": 3
},
"commitTokenThreshold": 20000,
"commitKeepRecentCount": 10,
"profileTokenBudget": 10000,
"resumeContextBudget": 32000
}
autoRecall.limit is a legacy quota-scaling input, not a final result cap.
Explicit values from 1 through 5 produce an effective total quota of 6 because
each coding category keeps one retrieval slot. Use Context quotas directly
when exact category ceilings are required.
API keys are resolved from environment variables or ~/.openviking/ovcli.conf and sent as Authorization: Bearer ... by both hooks and the MCP proxy. Recall goes through the server-side context face (POST /api/v1/search/search with mode="context"), falling back to the deprecated /api/v1/search/recall on older deployments. account and user are trusted-mode identity
headers sent as X-OpenViking-Account and X-OpenViking-User; leave them empty
when using API-key mode with user/admin API keys.
By default the plugin derives a peer from the git identity of the project directory: the normalized origin URL, else the repository root path. Outside a git repository no peer is sent at all, and what is remembered there goes to the user-level space viking://user/<you>/memories. git@github.com:volcengine/OpenViking.git becomes github.com-volcengine-openviking; the path fallback keeps the older naming rule where every non-letter-or-digit character becomes -, so /Users/x/Dev/OpenViking becomes -Users-x-Dev-OpenViking. One repository therefore keeps one peer across subdirectories, worktrees, clones and machines, while a fork's different origin keeps it separate. Derivation reads .git directly, so no git binary is needed. The plugin does not read workspace .openviking/config.json files, so a peer.id written there has no effect. Data-plane memory/resource requests send the effective peer as X-OpenViking-Actor-Peer; captured session messages store it as body peer_id. Configure peerId or OPENVIKING_PEER_ID to override the derived peer, or set workspacePeer=false / OPENVIKING_WORKSPACE_PEER=0 to send no peer at all. Memories written under the older directory-derived peer stay reachable: the default broad recall sweeps every peer under the user, and recallPeerScope="actor" asks that previous peer separately.
Recall defaults to the broad mode: global memory, the current workspace, and
other workspace memories can all be recalled, with other workspaces penalized
and rendered later. Set recallPeerScope="actor" or
OPENVIKING_RECALL_PEER_SCOPE=actor for the isolation mode, which only sees
global memory plus the current workspace. In deployments where one bot serves
multiple real people, such as zouk, vikingbot, or AstrBot, use the isolation mode
with an explicit actor peer so one person's memories are not recalled into
another person's session.
OPENVIKING_API_KEY, OPENVIKING_ACCOUNT, OPENVIKING_USER,
and OPENVIKING_PEER_ID take precedence over values in this file. The config
file's peerId still applies whenever shared credentials (ovcli.conf or
environment variables) do not carry a peer of their own, so an authenticated
setup keeps writing peer-scoped data instead of dropping into the shared user
tree.
For advanced setups, OPENVIKING_PLUGIN_CONFIG can point to another config file path.
Hook-only mode
When OpenViking is already exposed through another MCP server, retain the lifecycle hooks while skipping this plugin's bundled MCP registration:
{
"mcp": { "enabled": false }
}
This leaves repository context, automatic recall, message capture, and lifecycle commits enabled.
It does not add or overwrite OpenCode's mcp.openviking entry.
OpenCode's local read, glob, and grep tools cannot read viking:// URIs.
When the agent accidentally tries that, the plugin blocks the filesystem tool
call and points it to the OpenViking MCP tools.
MCP Tools
OpenCode sees the OpenViking MCP server as openviking, so tool names are namespaced with openviking_.
openviking_search: deep semantic retrieval across memories, resources, and skills; usemode="context"for balanced, injection-ready context.openviking_find: fast semantic retrieval.openviking_remember: store important facts or decisions for memory extraction.openviking_read: read one or moreviking://files.openviking_list: list aviking://directory.openviking_tree: show aviking://directory tree.openviking_grep: exact text or regex search.openviking_glob: glob file matching.openviking_write: create, overwrite, or append to aviking://file.openviking_edit: exact string replacement in aviking://file.openviking_add_resource: add a URL, local file, sitemap, or feed.openviking_forget: delete aviking://URI after explicit user confirmation.openviking_list_watches/openviking_cancel_watch: inspect or cancel resource watches.openviking_health: check OpenViking server health.
The proxy forwards the server's real tools/list response; the plugin does not maintain a separate native tool list.
Runtime Files
The plugin writes runtime files to ~/.config/opencode/openviking/ by default:
openviking-memory.logopenviking-session-state.json
Set runtime.dataDir in config to override this directory.
Automated npm releases
Changes merged into upstream main under examples/opencode-plugin/ trigger
.github/workflows/opencode-plugin-release.yml. Releases use the same calendar
version convention as the OpenClaw plugin: YYYY.M.D, then YYYY.M.D-N for
subsequent releases on that date (Asia/Shanghai). No manual source version bump
is required. The workflow changes the version only in the package being published;
it does not commit generated version changes back to the repository.
Releases are serialized. A queued run checks out current main, so several quick
merges may be included in one package. The npm manifest records
openvikingSourceCommit; rerunning a successfully published commit skips publication.
Registry failures stop the workflow instead of treating an unavailable registry as
an unused version. Only upstream main can publish the official npm latest tag.
Manual dispatch on main retries a failed release using the existing npm credentials
or Trusted Publishing configuration.