packages/adapters/. Every adapter implements
RuntimeAdapter from @clawboo/executor, and only the server imports them; they reach users inside the bundled
server of the clawboo package.
Use this page when you change an adapter, need to know what a runtime really reports, or want the fault-injection
runtime for a test. The runtimes as users see them are on Choose a runtime, and the steps for a
new one are on Write a runtime adapter.
At a glance
“Found” means the binary is on
PATH or in a per-user install folder (for Hermes, the pip and pipx user
folders). A managed home lives at <clawboo home>/runtimes/<runtime id>/<agent id>. A Codex or Hermes run with no
agent identity gets a throwaway temporary home instead.
What every adapter shares
- Construction. The Claude Code, Codex, Hermes and Native adapters take
(driverFactory, healthCheck?)and create one driver per run. The OpenClaw adapter takes the Gateway client. The mock takes nothing. - Health.
health()races the check against two seconds. A timeout or a throw reads as unhealthy. - Runs.
start()returnsrunId: null(the mock returnsmock-<session key>).events()subscribes when called, binds the run id from the first frame that carries one (falling back to the session key), and buffers throughcreateAsyncQueue. - Control.
abort(),setModel()andwriteContext()go to the run’s driver. The Claude Code, Codex and Hermes drivers ignoresetModel(), because the model is fixed when the run starts, and theirwriteContext()writes a file into the run’s working folder (nothing when the run has none). - Steering. The per-run adapters steer through
steerThroughDriver. TheirsessionCodecserializes the session key and the last native session id. - Server wiring.
adapterFactoryFor(id)inapps/web/server/lib/runtimes/index.tsbuilds the non-OpenClaw adapters, and gives the CLI drivers the attach secret so their MCP URLs pass the access gate. OpenClaw adapters come from the server’s shared Gateway connection (buildOpenClawServerAdapter).
Approvals and isolation
All five real adapters declaretoolApproval: true, which describes the runtime and not Clawboo’s runs:
- Claude Code runs with
permissionMode: 'bypassPermissions'. - Codex runs with approval policy
neverand sandboxdanger-full-access; itscodex execfallback passes--dangerously-bypass-approvals-and-sandbox. - Hermes runs with
--yoloand--accept-hooks. - Clawboo Native asks a person before every shell command, and its shell tool is off unless an agent’s config turns it on.
- OpenClaw decides which commands ask first through its own exec policy; Clawboo shows those requests and relays your answer to the Gateway.
What each adapter reports as cost
The adapter reports what the runtime gives it; the host fills the gaps with estimates. The task runner servesPOST /api/runtimes/<id>/run and agent routines, and team chat covers team turns and delegations.
On both paths, a run that ends without a cost report (a Claude Code run that fails first, say) is estimated from
characters; on the task runner it also logs a
spend_unreported warning unless it was aborted. An OpenClaw
routine fire skips the task runner. Every fire that runs records an estimate from characters, whether it
succeeded, failed, timed out or was stopped, priced at the routine’s model or else the agent’s model in
openclaw.json.
A token estimate prices the tokens at the model’s rate. A model Clawboo has no price for gets the shared default
rate of $3 per million input tokens and $15 per million output tokens. A model the user’s own Ollama serves is
free: a Clawboo Native turn on the Ollama provider, or an ollama/ model id, is recorded at $0, unless its tag
marks one of Ollama’s cloud models (-cloud or :cloud). The run ledger records each figure as reported,
local, token-estimate or char-estimate. What users see is described in
Budgets and spend.
Claude Code
The driver (createClaudeCodeDriver) loads the Claude Agent SDK on demand. The published clawboo package does
not ship the SDK, so a run on an install without it fails with a message that says how to install it alongside
Clawboo. The SDK’s own total_cost_usd is passed through unchanged, and its error_max_turns result becomes
done with reason max_turns. A steer goes in as a user message with priority next, which the CLI reads at its
next turn boundary.
Clawboo provisions no home for Claude Code: the SDK runs as your user, with your own home folder and Claude
login. The connected Anthropic key, when there is one, is passed in the run’s environment.
Codex
The driver hosts each run incodex app-server and falls back to codex exec for a Codex without it. Only the
app-server path can be steered (turn/steer); on the fallback a steer is refused as rejected. A thread resumes
through thread/resume, or codex exec resume <id> on the fallback.
Each agent gets a managed CODEX_HOME, so session files survive between turns. The driver copies your
codex login credentials (~/.codex/auth.json) into it and never writes them back. A managed home with no
usable login and no API key fails fast with no Codex credentials provisioned for managed home <path> and asks
the user to run codex login. The adapter’s models list is informational; nothing in the host reads it.
Hermes
The driver runshermes chat -q <prompt> -Q once per run. In that quiet mode, response lines become message
frames, the session_id: line on stderr becomes the resume handle, and the final result is built from the exit
code. Hermes reports no tool calls and no token usage, so a Hermes run shows no tool activity and emits no cost
event.
Steering is stop and resume (interrupt-resume): the driver stops the process and resumes the same session with
the message as the next input, and the host still sees one run. A passive steer is refused, because it must never
stop a run. Each agent gets a managed HERMES_HOME where its skills and memory persist, and the driver writes all
four Clawboo MCP servers into that home’s mcp.json. Hermes ships its own scheduler, but Clawboo never
runs it for teammates.
Hermes works one task at a time from Clawboo’s board: its own task board is not synced, and Clawboo never starts
hermes gateway, so Hermes’s chat channels stay off. The home is seeded once from your ~/.hermes/config.yaml;
with no config, the driver picks --provider from whichever key is connected (OpenRouter, Anthropic or OpenAI).
Clawboo Native
The package holds the adapter, the event mapper and theAgentConfig schema; the conversation loop, provider
clients and MCP bridge live in apps/web/server/lib/runtimes/native/. Each agent’s config is stored in the
settings table under native-agent-config:<agent id> and validated with agentConfigSchema on every load, so a
corrupt value falls back to DEFAULT_AGENT_CONFIG instead of crashing a run.
- Fields.
systemPrompt,primaryProvider,primaryModel,fallbacks,envVar(the vault slot’s name, never the key),tools(memory,tools,tasksastrue,falseor'read',teamchat, an optionalshellthat is off when absent,custom),maxTurns, andbudgetUsd. AbudgetUsdvalue creates an agent-scope hard-cap budget when the agent is created;nullkeeps the track-and-warn default. - Providers.
KNOWN_PROVIDERSlistsanthropic,openai,openrouterandollama, plus the OpenAI-compatiblegoogle,xai,groq,mistral,together,cerebrasandmoonshot. The schema accepts any provider string, but only these route: an unknown primary provider fails withunknown provider: <id>.envVarForProviderreturns each provider’s key variable, andnullfor keyless Ollama. - Defaults. An agent created without a provider takes the user’s recorded default native provider, else the
first connected provider in
KNOWN_PROVIDERSorder.DEFAULT_AGENT_CONFIG(Anthropic,claude-haiku-4-5, read-only Tasks, no shell) only fills in for an agent with no stored config. - Cost. Each provider response produces a
turnframe with that response’s token delta. Clawboo prices it from a short list of pinned model ids. A turn the Ollama provider served costs an exact $0 and is flaggedlocal(Ollama’s cloud models excepted); any other model getscostUsd: nulland is estimated by the host. - Context window.
contextWindowTokensis a deliberate over-estimate, because one number cannot fit every provider. A run that overflows a smaller window ends with a typed error instead.
OpenClaw
The adapter drives agents over the server’s live OpenClaw Gateway connection. It takes a structuralOpenClawGatewayClient, so tests pass an in-memory double. Team chat builds one per delivery through
buildOpenClawServerAdapter() (which returns null while the Gateway is down), and the Routines wake bridge
builds one for OpenClaw routine fires.
- Start.
start()sendschat.sendwithdeliver: falseand a fresh idempotency key, the context placed above the message. An acknowledgement that did not start a run (accepted: false, or a no-run status) throws, so the caller can release the task instead of waiting for frames that never come. - Events.
events()subscribes at once and keeps observing the session across runs, rebinding the run id from each frame. - Control.
abort()sendschat.abortfor the bound run and alwayssessions.abortas a backstop.setModel()issessions.patch.writeContext()sets an agent file, which a running session does not re-read. - Steering. A steer is
chat.sendwithqueueMode: 'steer', delivered when the Gateway confirms the steer’s own run. Frames for a steer’s run id are kept out ofevents(). A steer still unread when the run ends is cancelled and reported asrun-ended, and so is a steer for a session this adapter has no live run on. - No worktrees, no task runner.
runtimeClass: 'connected-substrate'makes the task runner refuse OpenClaw (connected_substrate) before it claims anything, andworktrees: falsemeans OpenClaw works in its own Gateway-owned folders. - No MCP attach.
mcp: falsemeans the adapter attaches nothing. The OpenClaw agent source registers Clawboo’s Memory, Tasks and Tools servers in the Gateway config’smcp.servers, without an agent identity, so the MCP servers treat OpenClaw sessions as unverified callers. See @clawboo/mcp. - No cost events. The Gateway sends no token usage, so Clawboo estimates OpenClaw spend from characters.
mapFrameToRuntimeEvents is pure. The server’s session activity watcher also uses it on its own, to show tool
activity from OpenClaw runs Clawboo did not start (an OpenClaw cron job, a message from a chat channel).
Tool results from the
agent tool stream and from session.message frames are cut at 4,000 characters and end
with a note of how many were not recorded.
Mock (fault injection)
@clawboo/adapter-mock is a real runtime, clawboo-mock, not a test double. It reproduces silence, crashes, open
tool calls and slow starts through the same server paths a real runtime uses. It executes nothing: every tool
call is a synthesized event.
The server offers it only when CLAWBOO_ENABLE_MOCK_RUNTIME is exactly 1. Then team turns, delegations and agent
routines can route to an agent whose runtime is clawboo-mock, and GET /api/runtimes lists it under runtimes
(never under available). The dashboard draws runtime cards from its own catalog, so it never shows the mock,
and a test seeds that agent row directly (as the wake bridge tests do).
The run’s message and context carry the faults as directives, one per line: a !word at the start of a line,
optionally indented. Other lines are ignored, and a message with no directive ends in done: success with the
summary mock run complete.
Delays are capped at ten minutes. The mock declares no worktrees, no resume and no steering, and
setModel() and
writeContext() do nothing.
Related
- Write a runtime adapter: build and test a new adapter.
- The RuntimeAdapter trait: the interface and the server wiring checklist.
- Choose a runtime: the runtimes and their capabilities as users see them.
- What agents can do: the safety posture of each runtime.
- Budgets and spend: how real and estimated costs reach the dashboard.