examples/openclaw-plugin/README.md
Use OpenViking as OpenClaw's long-term context engine: automatic recall, session archive, memory extraction, semantic search, and RAG over a remote OpenViking server.
openclaw plugins install clawhub:@openviking/openclaw-plugin
openclaw openviking setup --base-url http://my-server:1933 --api-key sk-xxx --json
openclaw gateway restart
openclaw openviking status --json
That's it. The setup command activates the context-engine slot and validates the connection.
Install the OpenClaw plugin @openviking/openclaw-plugin for OpenViking remote memory. My server is at
http://my-server:1933and my API key issk-xxx.
The agent runs install → setup → restart → verify automatically. See INSTALL-AGENT.md.
| Stage | What happens |
|---|---|
Every turn (afterTurn) | New messages are appended to an OpenViking session; commit/extraction is threshold-triggered |
Explicit remember (memory_store) | Important long-term facts can be written and committed immediately |
On /compact (compact) | Pending session messages are committed and extracted into long-term memories |
Before each reply (assemble) | Relevant memories are auto-retrieved and injected into context |
Once installed, the plugin provides these agent tools:
| Tool | Purpose |
|---|---|
memory_recall | Explicit long-term memory search |
memory_store | Persist explicit long-term facts immediately |
memory_forget | Delete memories by URI or query |
ov_archive_search | Search across archives by keyword |
ov_archive_expand | Expand an archive back to raw messages |
ov_recall_trace | Inspect why recall/search returned or injected specific results |
add_resource | Import documents, URLs, or Git repos when explicitly enabled |
add_skill | Import OpenViking skills |
ov_search | Search imported resources and skills |
ov_read | Read the full original content of one exact OpenViking URI |
ov_multi_read | Read the full original content of multiple OpenViking URIs |
ov_list | List OpenViking directories after search to inspect sibling chunks and overview files |
openviking_tool_result_read | Restore the full original content of an externalized tool result |
openviking_tool_result_search | Search inside an externalized tool result by keyword |
openviking_tool_result_list | List externalized tool results in the current session |
add_resource is hidden from agents by default (enableAddResourceTool=false), while manual /add-resource remains available. Configure recallTargetTypes to choose default recall targets (user, agent, resource); legacy recallResources=true appends resource only when recallTargetTypes is unset.
baseUrl). The plugin only sends data to that server; downstream model/provider data handling (embedding, VLM) depends on the server's configuration.viking://user/* (including viking://user/sessions/*) and viking://resources/*.X-API-Key header over your configured connection. Never logged or forwarded.accountId and userId. Optional peer_role / peer_prefix controls whether OpenClaw speakers are written as OpenViking peer_id.openclaw openviking status --json # one-shot health check
openclaw config get plugins.slots.contextEngine # should output: openviking
| Doc | Description |
|---|---|
| INSTALL.md | Full install, upgrade, and uninstall guide |
| INSTALL-ZH.md | Chinese install guide |
| INSTALL-AGENT.md | Agent-oriented operator guide |
| docs/openviking-openclaw-plugin-guide.md | Comprehensive Chinese guide for usage, configuration, debugging, testing, build, release, deployment, and rollback |
Plugin vs Skill: This page is for
@openviking/openclaw-plugin(the context-engine plugin). Do not useclawhub install openviking— that installs a different AgentSkill.
This plugin is registered as the openviking context engine in OpenClaw.
examples/openclaw-plugin is not a narrow "memory lookup" plugin. It is an integration layer that spans the OpenClaw lifecycle.In the current implementation, the plugin plays four roles at once:
context-engine: implements assemble, afterTurn, and compactsession_start, session_end, and before_resetThe diagram above reflects the current implementation boundary:
OpenVikingClient, which centralizes tenant headers and routing logs.viking://user/* (including viking://user/sessions/*) and viking://resources/*.That split lets OpenClaw stay focused on reasoning and orchestration while OpenViking becomes the source of truth for long-lived context.
The plugin keeps OpenClaw session identity in session and peer metadata. It does not send an OpenViking agent identity or create an agent namespace.
The main rules are:
sessionId directly when it is already a UUIDsessionKey when deriving a stable ovSessionIdpeer_role=assistant is the default and writes assistant messages with peer_id=<sessionAgent>; if peer_prefix is set, the value becomes <peer_prefix>_<sessionAgent>peer_role=none disables peer message attribution and actor-peer routingpeer_role=person writes user messages with peer_id derived from OpenClaw sender identity; assistant messages do not get peer_idX-OpenViking-Actor-Peer when peer_role is assistant or personmain for local session and assistant peer metadataX-OpenViking-Account / X-OpenViking-User when accountId / userId are explicitly configuredThis matters because OpenViking tenant identity is account/user-scoped, while OpenClaw agent identity is runtime metadata.
The recommended remote-mode configuration only needs:
baseUrlapiKeypeer_rolepeer_prefix when peer_role=assistantIn this setup:
apiKey should usually be a user keypeer_role=assistantaccountId / userId are advanced options only when the deployment needs explicit identity headers, such as root-key or trusted-server flowsThe plugin writes and searches user-scoped memory through viking://user/...; OpenViking resolves that alias from the request tenant and actor-peer context. viking://agent/... is deprecated by OpenViking and is not used by the plugin.
Auto-recall now runs through assemble(). OpenClaw calls the same context engine method in two shapes, and the plugin assigns different responsibilities to each shape:
prompt; messages is still old history. The plugin reads archive/session context back from OpenViking and rebuilds history.prompt; the latest messages entry is already the current user turn. The plugin only runs long-term recall and prepends the memory block to that user message content.During recall, the plugin:
sessionId/sessionKey.recallTargetTypes (user,agent by default; optionally resource; use ov_archive_search and ov_archive_expand for session history).## Long-term Memories section inside <openviking-context> to the current user message; it does not append a standalone synthetic user message.The reranking logic is not pure vector-score sorting. The current implementation also considers:
level == 2Session handling is the main axis of this design. In the current implementation it covers history assembly, incremental append, asynchronous commit, and blocking compaction readback.
assemble() doesDuring preflight, assemble() is not just replaying old chat history. It reads session context back from OpenViking under a token budget, then rebuilds OpenClaw-facing messages:
latest_archive_overview becomes [Session History Summary]pre_archive_abstracts becomes [Archive Index]toolCall (input compatible: toolUse/input is normalized to toolCall/arguments)toolResultThat means OpenClaw sees "compressed history summary + archive index + active messages", not an ever-growing raw transcript.
afterTurn() doesafterTurn() has a narrower job: append only the new turn into the OpenViking session.
user / assistant capture texttoolCall / toolResult content in the serialized turn text<openviking-context> blocks, historical <relevant-memories> blocks, and metadata noise before captureAfter that, the plugin checks pending_tokens. Once the session crosses commitTokenThreshold, it triggers commit(wait=false):
logFindRequests is enabled, the logs include the task id and follow-up extraction detailThis automatic path is best-effort and commit-dependent. Short but important facts can stay only in the live session until a threshold commit, /compact, or an explicit store happens.
When the user explicitly asks the agent to remember, save, or store an important long-term fact, preference, project, or decision, prefer memory_store over waiting for normal auto-capture. memory_store writes the text to an OpenViking session and calls commit(wait=true), so it is the reliable integration-side path for facts that should be available as long-term memory as soon as possible.
Use it as a complement to auto-capture, not a replacement:
memory_store is for explicit durable-memory intent such as "remember my main project is X" or "save this preference"memory_store commits but extracts 0 memories, check the OpenViking server extraction/model configuration; the explicit path triggered extraction, but the extractor did not produce a memorycompact() doescompact() is the stricter synchronous boundary:
commit(wait=true) and blocks for completionlatest_archive_overviewov_archive_expand to reopen a specific archiveSo afterTurn() is closer to "incremental append plus threshold-triggered async commit", while compact() is the explicit "wait for archive and compaction to finish" boundary.
Beyond automatic behavior, the plugin exposes these tools directly:
memory_recall: explicit long-term memory searchmemory_store: write explicit long-term facts into an OpenViking session and trigger commitmemory_forget: delete by URI, or search first and remove a single strong matchov_archive_expand: expand a concrete archive back into raw messagesov_recall_trace: inspect recent recall/search trace records when traceRecall is enabledadd_resource: import a document, directory, URL, or Git repository as an OpenViking resource when explicitly enabledadd_skill: import or register an OpenViking skillov_search: search OpenViking resources and skills, especially after importing themov_read: read one exact viking:// URI returned by ov_search or ov_listov_multi_read: read multiple exact viking:// URIs, useful for an overview plus sibling chunksov_list: list a hit's parent directory after ov_search to recover sibling chunks, .overview.md, and related split-document contextThey serve different roles:
memory_recall gives the model an explicit follow-up search pathmemory_store is for immediately persisting clearly important information when the user expresses durable-memory intentov_archive_expand is the "go back to archive detail" escape hatch when summaries are not enoughadd_resource lets the agent save explicit document or repository import requests without asking the user to remember slash commandsadd_skill imports skills into OpenViking, while add_resource imports resourcesov_search closes the loop after import by letting the user or agent confirm and consume resources and skillsov_read turns a ranked hit into original evidence before answering precise documentation, codebase, configuration, or procedural questionsov_multi_read reads overview and sibling chunks together when a split document needs more context than a single hitov_list complements ov_search when a ranked hit is only one chunk of a larger procedure or documentov_archive_expand is especially important because assemble() normally returns archive summaries and indexes, not the full raw transcript.
Resource and skill imports are intentionally separate because they land in different OpenViking namespaces and use different server APIs:
/api/v1/resources and land under viking://resources/.../api/v1/skills and land under viking://user/skills/...The plugin also registers explicit slash commands for manual imports:
/add-resource ./README.md --to viking://resources/openviking-readme --wait
/add-skill ./skills/install-openviking-memory --wait
/ov-search "OpenViking install" --uri viking://resources/openviking-readme
/ov-search "memory install skill" --uri viking://user/skills
Resource import supports remote URLs, Git URLs, local files, local directories, and uploaded zip files. OpenViking's built-in parsers cover common documents and media such as Markdown, text, PDF, HTML, Word, PowerPoint, Excel, EPUB, images, audio, and video. Directory imports also accept common code, documentation, and config file extensions such as .py, .js, .ts, .go, .rs, .java, .cpp, .json, .yaml, .toml, .csv, .rst, .proto, .tf, and .vue.
For HTTP safety, the plugin never sends a direct local filesystem path to the OpenViking server. Local files and directories are first uploaded through /api/v1/resources/temp_upload; directories are zipped locally with a pure JavaScript zip implementation before upload.
The plugin operates exclusively in remote mode as a pure HTTP client:
baseUrl and optional apiKey come from plugin configThe OpenViking service must be deployed and running independently before the plugin can connect to it.
The repo also contains a more future-looking design draft at docs/design/openclaw-context-engine-refactor.md. It is important not to conflate the two:
assemble(): preflight rebuilds history, transformContext injects long-term memoriesafterTurn() already appends to the OpenViking session, but commit remains threshold-triggered and asynchronous on that pathcompact() already uses commit(wait=true), but it is still focused on synchronous commit plus readback rather than owning every orchestration concernThat distinction matters, otherwise the future design draft is easy to misread as already shipped behavior.
If you need to debug this plugin, start with these entry points.
openclaw openviking status --json
openclaw plugins list
openclaw config get plugins.entries.openviking.config
openclaw config get plugins.slots.contextEngine
OpenClaw plugin logs:
openclaw logs --follow
OpenViking service logs:
cat ~/.openviking/data/log/openviking.log
python -m openviking.console.bootstrap --host 0.0.0.0 --port 8020 --openviking-url http://127.0.0.1:1933
ov tuiov tui
| Symptom | More likely cause | First check |
|---|---|---|
plugins.slots.contextEngine is not openviking | The plugin slot was never set, or another plugin replaced it | openclaw config get plugins.slots.contextEngine |
| Cannot connect to OpenViking service | baseUrl is wrong or the service is down | Check baseUrl in config and test connectivity manually |
| recall behaves inconsistently across sessions | Routing identity is not what you expected | Enable logFindRequests, then inspect openclaw logs --follow |
| long chats stop extracting memory | pending_tokens never crosses the threshold, or Phase 2 fails server-side | Check plugin config and ~/.openviking/data/log/openviking.log |
| summaries are too coarse for detailed questions | You need archive-level detail, not just summary | Use an ID from [Archive Index] with ov_archive_expand |
For installation, upgrade, and uninstall operations, use INSTALL.md.
</details>