Capabilities
Compatibility
Verification
Tags
OpenClaw + OpenViking Context-Engine Plugin
Use OpenViking as the long-term memory backend for OpenClaw. In OpenClaw, this plugin is registered as the openviking context engine.
This document is not an installation guide. It is an implementation-focused design note for integrators and engineers. It describes how the plugin works today based on the code under examples/openclaw-plugin, not a future refactor target.
Documentation
- Install and upgrade: INSTALL.md
- Chinese design and install guide: INSTALL-ZH.md
- Agent-oriented operator guide: INSTALL-AGENT.md
Design Positioning
- OpenClaw still owns the agent runtime, prompt orchestration, and tool execution.
- OpenViking owns long-term memory retrieval, session archiving, archive summaries, and memory extraction.
examples/openclaw-pluginis 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: implementsassemble,afterTurn, andcompact- hook layer: handles
before_prompt_build,session_start,session_end,agent_end, andbefore_reset - tool provider: registers
memory_recall,memory_store,memory_forget, andov_archive_expand - runtime manager: starts and monitors an OpenViking subprocess in
localmode
Overall Architecture

The diagram above reflects the current implementation boundary:
- OpenClaw remains the primary runtime on the left. The plugin does not take over agent execution.
- The middle layer combines hooks, the context engine, tools, and runtime management in one plugin registration.
- All HTTP traffic goes through
OpenVikingClient, which centralizesX-OpenViking-*headers and routing logs. - The OpenViking service owns sessions, memories, archives, and Phase 2 extraction, with storage under
viking://user/*,viking://agent/*, andviking://session/*.
That split lets OpenClaw stay focused on reasoning and orchestration while OpenViking becomes the source of truth for long-lived context.
Identity and Routing
The plugin does not send one fixed agent ID to OpenViking. It tries to keep OpenClaw session identity and OpenViking routing aligned.
The main rules are:
- reuse
sessionIddirectly when it is already a UUID - prefer
sessionKeywhen deriving a stableovSessionId - normalize unsafe path characters, or fall back to a stable SHA-256 when needed
- resolve
X-OpenViking-Agentper session, not per process - when
plugins.entries.openviking.config.agentIdis notdefault, prefix the session agent as<configAgentId>_<sessionAgent> - add
X-OpenViking-Account,X-OpenViking-User, andX-OpenViking-Agentin the client layer
This matters because the plugin is built to support multi-agent and multi-session OpenClaw usage without mixing memories across sessions.
Prompt-Front Recall Flow

Today the main recall path still lives in before_prompt_build:
- Extract the latest user text from
messagesorprompt. - Resolve the agent routing for the current
sessionId/sessionKey. - Run a quick availability precheck so prompt building does not stall when OpenViking is unavailable.
- Query both
viking://user/memoriesandviking://agent/memoriesin parallel. - Deduplicate, threshold-filter, rerank, and trim the results under a token budget.
- Prepend the selected memories as a
<relevant-memories>block.
The reranking logic is not pure vector-score sorting. The current implementation also considers:
- whether a result is a leaf memory with
level == 2 - whether it looks like a preference memory
- whether it looks like an event memory
- lexical overlap with the current query
Transcript ingest assist
This path also includes a special transcript-oriented branch.
When the latest user input looks like pasted multi-speaker transcript content:
- metadata blocks, command text, and pure question text are filtered out
- the cleaned text is checked against speaker-turn and length thresholds
- if it matches, the plugin prepends a lightweight
<ingest-reply-assist>instruction
The goal is not to change memory logic. It is to reduce the chance that the model responds with NO_REPLY when the user pastes chat history, meeting notes, or conversation transcripts for ingestion.
Session Lifecycle

Session handling is the main axis of this design. In the current implementation it covers history assembly, incremental append, asynchronous commit, and blocking compaction readback.
What assemble() does
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_overviewbecomes[Session History Summary]pre_archive_abstractsbecomes[Archive Index]- active session messages stay in message-block form
- assistant tool parts become
toolUse - tool output becomes separate
toolResult - the final message list goes through a tool-use/result pairing repair pass
That means OpenClaw sees “compressed history summary + archive index + active messages”, not an ever-growing raw transcript.
What afterTurn() does
afterTurn() has a narrower job: append only the new turn into the OpenViking session.
- it slices only the newly added messages
- it keeps only
user/assistantcapture text - it preserves
toolUse/toolResultcontent in the serialized turn text - it strips injected
<relevant-memories>blocks and metadata noise before capture - it appends the sanitized turn text into the OpenViking session
After that, the plugin checks pending_tokens. Once the session crosses commitTokenThreshold, it triggers commit(wait=false):
- archive generation and Phase 2 memory extraction continue asynchronously on the server
- the current turn is not blocked waiting for extraction
- if
logFindRequestsis enabled, the logs include the task id and follow-up extraction detail
What compact() does
compact() is the stricter synchronous boundary:
- it calls
commit(wait=true)and blocks for completion - when an archive exists, it re-reads
latest_archive_overview - it returns updated token estimates, the latest archive id, and summary content
- if the summary is too coarse, the model can call
ov_archive_expandto reopen a specific archive
So afterTurn() is closer to “incremental append plus threshold-triggered async commit”, while compact() is the explicit “wait for archive and compaction to finish” boundary.
Tools and Expandability
Beyond automatic behavior, the plugin exposes four tools directly:
memory_recall: explicit long-term memory searchmemory_store: write text 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 messages
They serve different roles:
- automatic recall covers the default case where the model does not know what to search yet
memory_recallgives the model an explicit follow-up search pathmemory_storeis for immediately persisting clearly important informationov_archive_expandis the “go back to archive detail” escape hatch when summaries are not enough
ov_archive_expand is especially important because assemble() normally returns archive summaries and indexes, not the full raw transcript.
Local / Remote Runtime Modes

The current implementation supports two runtime modes. The upper-layer session, memory, and archive model stays the same in both.
Local mode
In local mode, the plugin manages the OpenViking subprocess itself:
- resolve Python from
OPENVIKING_PYTHON, env files, or system defaults - prepare the port before startup
- kill stale OpenViking processes on the target port
- move to the next free port if another process owns the configured one
- wait for
/healthbefore marking the service ready - cache the local client so multiple plugin registrations do not spawn duplicates
That is why this plugin is not only “memory logic”. It is also a local runtime manager.
Remote mode
In remote mode, the plugin behaves as a pure HTTP client:
- no local subprocess is started
baseUrland optionalapiKeycome from plugin config- session context, memory find/read, commit, and archive expansion behavior stays the same
The main difference between local and remote is who is responsible for bringing up the OpenViking service, not the higher-level context model.
Relationship to the Older Design Draft
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:
- this README describes current implemented behavior
- the older draft discusses a stronger future move into context-engine-owned lifecycle control
- in the current version, the main automatic recall path still lives in
before_prompt_build, not fully inassemble() - in the current version,
afterTurn()already appends to the OpenViking session, but commit remains threshold-triggered and asynchronous on that path - in the current version,
compact()already usescommit(wait=true), but it is still focused on synchronous commit plus readback rather than owning every orchestration concern
That distinction matters, otherwise the future design draft is easy to misread as already shipped behavior.
Operator and Debugging Surfaces
If you need to debug this plugin, start with these entry points.
Inspect the current setup
ov-install --current-version
openclaw config get plugins.entries.openviking.config
openclaw config get plugins.slots.contextEngine
Watch logs
OpenClaw plugin logs:
openclaw logs --follow
OpenViking service logs:
cat ~/.openviking/data/log/openviking.log
Web Console
python -m openviking.console.bootstrap --host 0.0.0.0 --port 8020 --openviking-url http://127.0.0.1:1933
ov tui
ov tui
Common things to check
| 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 |
local mode fails to start | Python path, env file, or ov.conf is wrong | source ~/.openclaw/openviking.env && openclaw gateway restart |
| 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.
