Skip to content

Architecture ​

OpenVole follows a microkernel architecture — the core provides the agent loop and the plugin contract, nothing else. Everything useful (reasoning, memory, tools, channels, integrations) is a Paw or a Skill.

System Diagram ​

The Agent Loop ​

The core of OpenVole — a 6-phase loop that runs per task:

Bootstrap ─┐
           ▼
Perceive → Compact → Think → Act → Observe → loop
PhaseWhat happensRuns
BootstrapPaw hooks load persistent data (memory, session history). VoleNet context injected.Once per task
PerceivePaw hooks enrich context with dynamic data (time, calendar, unread messages).Every iteration
CompactTriggered when message count exceeds compactThreshold. Compresses old messages to free context space.When needed
ThinkCore builds system prompt, calculates token budget, trims by priority. Brain Paw calls LLM, returns AgentPlan (tool calls + optional response).Every iteration
ActCore executes tool calls (sequential or parallel). Applies rate limits. Remote tools route via VoleNet.Every iteration
ObservePaw hooks process results (update memory, log to session, notify channels). Session sync to VoleNet peers.Every iteration

The loop exits when:

  • The Brain produces a final answer with no tool calls
  • maxIterations is reached (resets on successful tool execution)
  • The task is cancelled

Context Budget ​

The ContextBudgetManager handles token-aware context management:

  1. Token estimation — estimates token count for system prompt, tools, messages, session
  2. Priority trimming — when context exceeds maxContextTokens, trims in order:
    • Old tool results (lowest priority — trimmed first)
    • Old error messages
    • Old assistant/brain messages
    • Session history
  3. Never trimmed — system prompt, first user message, last 2 brain responses
  4. Response reserve — responseReserve tokens kept for the Brain's output

Tool Horizon ​

When toolHorizon: true (default), the Brain starts with only core tools visible. It discovers additional tools on demand via discover_tools with an intent query. This prevents context bloat when many paws are loaded — the Brain only sees tools relevant to the current task.

Cost Tracking ​

The CostTracker records LLM token usage and cost per task:

  • Tracks input/output tokens and cost per LLM call
  • Supports per-provider pricing (cloud APIs vs local Ollama)
  • costAlertThreshold warns when a single task exceeds a USD amount
  • Configurable via costTracking: "auto", "enabled", "disabled"

System Prompt ​

Core builds the system prompt — Brain Paws are thin API adapters that receive it pre-built via context.systemPrompt.

The prompt is assembled from:

  1. BRAIN.md — custom system prompt (overrides default if present)
  2. Identity files — SOUL.md, USER.md, AGENT.md
  3. Skills — available skill summaries
  4. Tools — tool descriptions and parameters
  5. Memory — agent memory context
  6. VoleNet — instance name, role, peer list with tools and brain status
  7. Date/time/platform — dynamic context

Paws ​

Paws are subprocess-isolated plugins. They connect OpenVole to the outside world — APIs, databases, browsers, messaging platforms. Each Paw runs in its own Node.js process with --permission sandbox.

Paws can:

  • Register tools with the Tool Registry
  • Register lifecycle hooks (onBootstrap, onPerceive, onCompact, onObserve)
  • Inject context into the Brain's prompt via context.metadata
  • Discover and register tools at runtime (late tool registration)

There are four categories of Paws:

CategoryExamplesRole
Brainpaw-brain (unified: Ollama, Claude, OpenAI, Gemini, xAI, Claude Code, Antigravity)LLM reasoning via the Think phase
ChannelTelegram, Slack, DiscordReceive messages from external platforms
ToolBrowser, Shell, MCP, Database, Email, Scraper, ImageTools the Brain can call
InfrastructureMemory, Session, Compact, DashboardLifecycle hooks and internal services

Paw Sandbox ​

Each Paw process is launched with Node.js --permission flags:

  • Filesystem — restricted to .openvole/paws/<name>/ plus explicitly allowed paths
  • Network — restricted to explicitly allowed domains/IPs
  • Child processes — blocked unless childProcess: true
  • Environment — only explicitly listed env vars are passed

Optional Docker sandbox available for stronger isolation.

Skills ​

Skills are behavioral recipes. A Skill is a folder with a SKILL.md file — no build step — optionally with bundled scripts the Brain runs via skill_run_script, sandboxed to the skill's own directory. They tell the Brain how to approach a task by providing instructions, not tools.

Skills activate based on available tools — a skill requiring email_send only loads when an email paw is present. The Brain sees a list of available skills and can load full instructions on demand using the skill_read tool.

Tools ​

Tools are the runtime abstraction. Every action the Brain can take is a tool — whether it came from a Paw, from the core, from an MCP server, or from a remote VoleNet peer. The Brain doesn't know the difference.

Built-in Core Tools ​

ToolPurpose
discover_toolsSearch available tools by intent (BM25 ranking)
schedule_taskCreate recurring tasks at runtime
cancel_schedule / list_schedulesManage schedules
skill_readLoad skill instructions on demand
skill_read_reference / skill_list_filesAccess skill resources
skill_run_scriptRun a script bundled in a skill, confined to its directory
heartbeat_read / heartbeat_writeRead/write recurring job definitions
workspace_write / workspace_readRead/write agent scratch space
workspace_list / workspace_deleteList/delete workspace files
vault_storeStore a secret (write-once, with optional metadata)
vault_get / vault_list / vault_deleteRetrieve, list, or delete vault entries
web_fetchLightweight URL fetching (GET/POST with headers, body)
spawn_agentSpawn a sub-agent with a named profile
spawn_remote_agentDelegate a task to a remote VoleNet peer
list_instancesList connected VoleNet peers
get_remote_resultCheck status of a remote task

VoleNet ​

Distributed agent networking. Connects multiple OpenVole instances across machines. Every message is signed and authorized per peer, with hybrid post-quantum signatures (Ed25519 + ML-DSA-65 when the runtime supports it). Sealed envelopes — relayed traffic always, direct traffic with net.encrypt — add post-quantum hybrid confidentiality (X25519 + ML-KEM-768); TLS for the transport is optional on top.

Key capabilities:

  • Remote tool execution — tools on remote peers appear in the local registry
  • Memory sync — write propagation and remote search across peers
  • Session sync — shared conversations across devices
  • Brain sharing — brainless workers delegate thinking to a coordinator
  • Leader election — automatic failover, heartbeat coordination
  • Load balancing — tasks route to the least-loaded peer

See the VoleNet documentation for architecture patterns and setup.

Control Plane & Agents ​

vole serve runs the control plane — a single web server (the @openvole/dashboard-server package) that manages every agent from one place. This is the primary way to operate OpenVole.

  • A agent is an isolated agent — its own vole.config.json, paws, identity files, and data directory. Each running agent is its own engine subprocess (IPC child), parented to the vole serve process (not detached).
  • All agents under one OpenVole root are recorded in an agents.json registry. The control plane resolves the root from VOLE_HOME, else the current directory if it's already a root or empty (see the Dashboard guide).
  • The control plane aggregates each agent's state and events over IPC and serves one dashboard (Overview / Chat / Apps / Config / Identity) for all of them.

Orchestrator Agents (reverse-RPC) ​

An agent can be granted orchestrator authority (vole agent create <name> --orchestrator, or vole agent orchestrate <name> on|off), letting its agent manage the sibling agents under the same vole serve. The mechanics:

  • The flag is stored in the server's agents.json registry — outside every agent's sandbox, so an agent can't grant itself authority.
  • The control plane spawns a flagged agent with VOLE_ORCHESTRATOR=1; the daemon then registers the agent_* core tools (list, state, submit, task_status, read/write config, read/write identity, restart, start, stop, create) backed by a reverse-RPC client on the same IPC channel the control plane already uses: the child sends {creq:{id,method,params}}, the parent answers {cres:{id,result|error}}.
  • The parent re-reads the registry and verifies the sender's flag on every request — revoking is instant. Requests targeting other agents reuse the exact per-agent RPC path the dashboard uses, so its guards (demo mode, sandbox-weakening refusal) apply unchanged.
  • Hard limits: no self stop/start/restart, no agent removal, and agents created by an orchestrator are never flagged themselves.
  • The vole-orchestrate VoleHub skill (requires agent_list/agent_submit) supplies the supervisor playbook and only activates in agents that actually have the tools.

Agent Conversations (agent_message) ​

Orchestration is management; messaging is not. Every agent gets agent_message, whether or not it is an orchestrator — talking to a colleague is not a privilege, and the agent_* management family stays behind the flag.

  • A message lands in a thread and wakes the agent. The pair's conversation is a session named from each side (agent:orchestrator on the worker, agent:video-editor on the coordinator), so both keep their own history and no shared registry is needed. Waking is the default: a person's chat message already works that way, and a colleague's word should not sit unread for having arrived over a different channel.

  • A hop budget ends the exchange, not restraint. A reply is itself a message, so if every arrival wakes the receiver, two agents will politely answer each other's answers at one brain call per turn. The count rides with the message and is enforced at MAX_AGENT_HOPS (4) — enough for ask → clarify → answer → confirm. Past it the message is still delivered; only the waking stops, so the last word is read on the next run rather than lost.

  • A run started by a message answers the agent that sent it, the same way a chat turn answers its chat. One rule (replyAddressFor) decides where any run reports: its session, else the agent that wrote in, else the project, else the dashboard.

  • The answer reaches the person who asked. When a human's question is what sent an agent to a colleague, the asking run's reply address travels with the message and returns on the answer, and the colleague's reply is delivered straight into that human's chat — badge, live update and all. This does not depend on the agent remembering to relay, because it reliably did not: the reply arrives in a run addressed to the colleague, so relaying was something the model had to think of.

    The address is spent on that answer. It used to travel on, which made every later turn count as "on behalf of" the person — so the colleagues' wind-down ("sounds good", "happy to help") was relayed too. One question, one answer; the agents may keep talking, and that conversation stays in their own thread.

  • Provenance comes from the run, never from the caller. An agent that could name its own relay target could post into a conversation it was never part of. The reply address and hop count are read from the task being executed and attached by the control plane.

Brains that expose tools to a CLI

A brain running with CLAUDE_CODE_EXPOSE_TOOLS=1 calls its tools over the agent's MCP endpoint rather than through the loop. That path is stateless, so the executing side resolves the calling run from the task queue — and only when exactly one task is running. Above taskConcurrency: 1 a stateless call cannot say which run it came from, so no context is attached rather than the wrong one.

Messaging is filed under its own paw name (__agent_chat__), separate from __orchestrate__. The split is what lets anything inspecting the registry tell the two apart — VoleNet withholds both from the mesh by source (CONTROL_PLANE_PAWS), so a tool added to either family later cannot be missed by forgetting to update a name pattern.

Embedded Panels (Apps) ​

A paw can contribute a UI to the dashboard by declaring a panel in its manifest; the control plane serves the static HTML at /panel/<agent>/<paw>/ and proxies the paw's tools at /panel/<agent>/<paw>/tool/<toolName> — brain-free, directly over IPC. Everything flows through the one control-plane server, so there are no per-paw web servers and no extra ports. See Build an Embedded App for the authoring guide.

Philosophy ​

If it connects to something, it's a Paw.If it describes behavior, it's a Skill.If the agent calls it, it's a Tool.If it's none of these, it probably doesn't belong in OpenVole.