Runtimes
Five adapters, one line of config, no coupling.
A runtime is what actually executes a turn. Five ship. An app picks one with a single line, and nothing else in the workspace changes.
apps:
reviewer:
name: Rev
type: claude_code # ← the only line that decides who answers
soul: agents/reviewer/SOUL.md
Why you would care
Most agent frameworks are a commitment. You adopt one, you write your tools against its decorators, its schema, its session object — and moving costs a rewrite. That is a bet on a field that is changing every few months.
Runspace does not ask for that bet. The workspace, the channels, the message history, the scheduler, the widget rendering and the UI are all the same whichever runtime answers. Swapping is a one-line config change, not a migration, so you can:
- Keep the subscription you already pay for. If you have a Claude or Codex
CLI seat, the
claude_codeandcodexadapters use it. No second API bill to run a workspace. - Use a different runtime per agent. One workspace can run a Codex-backed reviewer beside an in-process Agentino analyst. They share the channel and neither knows about the other.
- Change your mind later. When something better ships, it is a new file and a config line, not a rewrite of your tools.
The five
type: | Executes | Needs | Binary override |
|---|---|---|---|
agentino | in-process, no subprocess | pip install agentino-framework | — |
codex | codex exec --json | the Codex CLI on PATH | CODEX_BIN |
claude_code | claude -p --output-format stream-json | the Claude Code CLI | CLAUDE_BIN |
pi | pi --print | the Pi CLI | PI_BIN |
openclaw | openclaw agent --local --json | the OpenClaw CLI | OPENCLAW_BIN |
The four CLI adapters shell out to a binary you install, so Runspace itself
depends on none of them — they are not in install_requires, and a workspace
that uses none of them installs none of them. agentino is an optional extra
for the same reason.
model: on the app overrides the runtime's default, so the same adapter can
run different models per agent. pi and openclaw accept a provider/model
form.
Choosing
agentino— you want tools as decorated Python functions in the same process, with no binary to install and no subprocess per turn. Start here.codex/claude_code— you already have the seat, and you want that model's coding behaviour without wiring an API key.pi/openclaw— you are already using them, or you want a runtime whose sandbox and tool policy are managed outside your process.
The boundary is enforced, not just intended
app_registry.py dispatches on app.type and imports no runtime — it imports
the adapter module, which is a different thing. Every module that does touch a
runtime imports it lazily, inside the function that needs it.
The result is checkable rather than asserted: install without the [agentino]
extra, import the gateway and the registry, and agentino is absent from
sys.modules. A test also reads the registry's source and fails if a runtime
import appears there, because "we intend to stay decoupled" degrades quietly
and a failing test does not.
That is what makes a sixth adapter a new file rather than a refactor — and it is why the promise above is a property of the code rather than a claim in a README.
Writing a sixth
An adapter needs one async function:
async def chat(registry, app, message: str, session_id: str) -> dict:
return {"text": ..., "tools_used": [...]}
For a CLI adapter most of the work is parsing the other tool's output — and most of the maintenance is surviving the day that output changes shape. Degrade rather than raise: an unparseable response should return empty text, not take the workspace down on someone else's point release.