The brain — providers
How agents think — the Claude Code binary, the OpenAI Codex CLI, local models via Ollama and LM Studio, direct OpenAI-compatible servers, and per-agent brains.
Last updated
Every agent’s intelligence comes from a provider — the brain. The coordination layer around it (scheduling, review, budget) is the same no matter which brain an agent runs on, which is why you can mix expensive and cheap brains in one org.
The Claude Code binary
The default provider spawns the claude binary headless:
claude --print --output-format json # prompt on stdin
The binary owns its own agentic loop, so on a task an agent acts — it runs
Bash, writes and executes code, and edits files in its working directory
(agents/<id>/). The single JSON result is parsed for the answer plus
total_cost_usd / usage, which is metered to the cost ledger.
Task turns are session-continuous: the result’s session_id is stored on
the task, and the next attempt at the same task — a rework after review, a
retry after an error, a re-open after an operator answer — resumes it with
--resume, sending only what changed instead of re-paying for the full brief.
The session is keyed to the brain that minted it, so changing the agent’s
model, effort, or persona starts fresh. See
Orchestration for how the loop uses this.
Auth comes from the binary’s own login or ANTHROPIC_API_KEY. Set CLAUDE_BIN
(or --claude-bin) if claude isn’t on your PATH.
Under the hood a brain is used in two ways:
- A plain text turn — used for meetings, evaluation, and decomposition.
- A full agentic turn — real work in the agent’s working directory, with tools gated by the agent’s capabilities.
The OpenAI Codex CLI
Give an agent the codex binary with { "type": "codex" }. Quorum drives it
headless the same way — one spawn, prompt on stdin:
codex exec --json # prompt on stdin, JSONL events on stdout
Codex also owns its own agentic loop, so a task agent runs shell commands and edits files in its working directory. Two differences from the Claude binary:
- Sandbox, not an allowlist. Where Claude takes a tool allowlist, Codex takes
a sandbox policy. Quorum translates the agent’s granted
capabilities automatically: an agent with the
codecapability runsworkspace-write(it can edit files and run commands in its folder); without it,read-only. Override per agent withsandbox. - Tokens, priced by you. Codex reports token
usagebut not a dollar figure. Supply apricingrate to get a USD cost on the ledger; without one, tokens are tracked and cost counts as$0.
Auth comes from the binary’s own login (codex login) or OPENAI_API_KEY. Set
CODEX_BIN if codex isn’t on your PATH.
### Engineer
- provider: { "type": "codex", "model": "gpt-5-codex" }
Local models — Ollama & LM Studio
A brain doesn’t have to be in the cloud. Because a provider is just a CLI that owns its loop, the same harness — with its tools, sandbox, and metering — can run on a local model, so an org (or a few cheap roles in it) costs nothing to run. There are two routes:
Codex, natively. Codex has built-in local support — point it at Ollama or LM Studio and pick a model you’ve pulled:
### Researcher
- provider: { "type": "codex", "oss": true, "localProvider": "ollama", "model": "gpt-oss:20b" }
### Analyst
- provider: { "type": "codex", "oss": true, "localProvider": "lmstudio", "model": "qwen2.5-coder" }
Claude, via ollama launch. Ollama can launch the Claude Code harness backed
by a local model. Set bin to ollama and a launch prefix; Quorum runs
ollama launch claude … -- in front of the standard headless call, and the
launcher’s --model selects the brain:
### Writer
- provider: { "type": "claude", "bin": "ollama", "launch": "launch claude -y --model gemma4 --" }
Local models are free, so their calls show tokens with a $0 cost. One honest
caveat: small local models are weaker at multi-step tool use — they may talk
through a task instead of running the commands to do it. Use them for the
deliberative roles (research, review, meetings) and a cloud brain for the heavy
building, or size up the local model. Mixing the two in one org is the point:
### Strategist # local, free — does the thinking
- provider: { "type": "codex", "oss": true, "localProvider": "ollama", "model": "gpt-oss:20b" }
### Engineer # cloud — does the building
- provider: { "type": "claude", "model": "claude-opus-5" }
Both routes run a full agentic harness on the local model. If a deliberative
role just needs local text — no tools — there’s a third route with no binary
at all: point the openai brain (see Direct HTTP below) straight at LM
Studio’s, vLLM’s, or Ollama’s OpenAI-compatible endpoint.
More agent CLIs
Beyond Claude and Codex, Quorum can drive the wider ecosystem of headless coding agents as brains. Each owns its own tool loop; Quorum spawns it, feeds the prompt, and reads its result:
type |
binary | reports |
|---|---|---|
opencode |
opencode | text + tokens + cost |
cline |
cline | text + tokens + cost |
copilot |
GitHub Copilot CLI | text + output tokens (bills “premium requests”, no USD) |
cursor |
Cursor CLI (cursor-agent) |
text only (no tokens/USD reported) |
grok |
Grok Build (xAI) | text (streaming-json) |
qwen |
Qwen Code (Alibaba) | text + tokens (stream-json) |
amp |
Amp (Sourcegraph) | text (stream-json) |
pi |
pi (Earendil) | text + tokens + cost |
goose |
goose (Block / Linux Foundation) | text + total tokens |
hermes |
Hermes Agent | text only |
kimi |
Kimi Code CLI | text + tokens (stream-json) |
droid |
Factory Droid | text + tokens |
antigravity |
Antigravity CLI (agy) |
text + tokens (JSON) |
dsh |
DeepSeek Harness | text only |
llm |
llm (Simon Willison) |
text only — a lightweight text brain |
opencode, cline, copilot, hermes, llm, antigravity, and dsh are
verified headlessly against their live CLIs. For kimi, droid, cursor, grok,
qwen, amp, pi, and goose the flags and output format are verified
against the shipped CLIs, without a live end-to-end run — give one a quick task
against your own account before relying on it unattended.
llm is the odd one out: it has no tool loop, just text in → text out. That
makes it a cheap, reliable pick for the deliberative roles — meetings,
evaluation, routing, writing — where a full agent is overkill. Give it a local
model with the llm-ollama plugin (llm install llm-ollama, then
"model": "gpt-oss:20b"); its token counts print to stderr, so calls meter free.
The rest configure exactly like any other brain. Point one at its own cloud backend with a model and its own auth:
### Reviewer
- provider: { "type": "opencode", "model": "anthropic/claude-sonnet-5" }
### Researcher
- provider: { "type": "antigravity", "model": "Gemini 3.5 Flash (Low)" }
Antigravity’s model is the display name listed by agy models (e.g.
Gemini 3.5 Flash (Low), Claude Opus 4.6 (Thinking)), and its binary is agy
— set ANTIGRAVITY_BIN if it isn’t on your PATH.
dsh is DeepSeek’s open-source agent runtime, where everything is a plugin and
a profile is an ordered stack of those plugins. Quorum drives its shipped
headless profile, which answers one task, prints the final message, and exits.
It is the one brain here with no flags of its own, so three things that are options elsewhere are configuration:
- Model — set by the profile’s
agent-default-modelplugin (it defaults todeepseek-v4-flashon DeepSeek’s own API, withDEEPSEEK_API_KEYfrom the environment). Aprovider.modelis therefore ignored unless a launcher owns it; to change it directly, pass a config overlay inargs:{ "type": "dsh", "args": ["--patch", "./model.yml"] }. - Persona — there is no system-prompt flag, so the role’s persona is folded into the task text.
- Permissions — the profile’s plugin stack decides what tools exist, so a
headless turn runs tools and writes files regardless of the agent’s
capabilities. Quorum cannot make it read-only from the command line. Give a
read-only role a different brain, or point
dshat a profile whose stack leaves the write plugins out.
For a local model, let Ollama own the launch — it installs the harness on first run and wires the model up:
### Builder
- provider: { "type": "dsh", "launch": "launch dsh --model deepseek-v4-flash:cloud --" }
It reports neither tokens nor dollars, so calls meter free unless you set
pricing. It is a developer preview (verified against 0.1.0-rc.6), so re-check
the flags after an upgrade.
cursor is the route to Cursor’s account models — including Composer,
Cursor’s own agentic model, which has no standalone API. Its binary is
cursor-agent, with auth via cursor-agent login or CURSOR_API_KEY. Leave
model unset to use your Cursor default, or pick an id from
cursor-agent --list-models:
### Builder
- provider: { "type": "cursor", "pricing": { "inputPerMTok": 0.5, "outputPerMTok": 2.5 } }
Cursor reports no token or dollar figures in its output, so calls meter free
unless you supply pricing (the example above is Composer’s published rate —
check yours).
A few of the others have their own conventions worth knowing:
grokreaches xAI’s Grok models — auth via an interactivegroklogin orGROK_CODE_XAI_API_KEYfrom the xAI console. Pick a model id fromgrok models, or leavemodelunset for your account default.qwenhas a free tier: runqwenonce interactively to complete the OAuth login, then the org can use it headlessly. Reports real token counts.amphas no model ids — its--modetiers (low,medium,high,ultra) pick the model and harness, so Quorum’smodelfield maps onto them:{ "type": "amp", "model": "high" }. Auth viaamp loginorAMP_API_KEY.pitakesmodelas"provider/id"(e.g."anthropic/claude-sonnet-5") and reads the provider’s usual API-key env var. It reports tokens and dollars per call — the best-metered brain in the family.goosetakes its provider from your goose config orGOOSE_PROVIDER; pairmodelwithargs: ["--provider", "anthropic"]to set both from QUORUM.md.
…or run any of them on a local model through the same ollama launch
prefix as Claude — bin: "ollama" + a launch string; the launcher selects the
model, so no inner model flag is emitted:
### Analyst
- provider: { "type": "opencode", "bin": "ollama", "launch": "launch opencode -y --model gpt-oss:20b --" }
### Scout
- provider: { "type": "hermes", "bin": "ollama", "launch": "launch hermes -y --model gpt-oss:20b --" }
Set <TOOL>_BIN (e.g. OPENCODE_BIN, CLINE_BIN) if a binary isn’t on your
PATH. As with Codex, a tool that reports tokens but not dollars gets a USD cost
only if you supply pricing; local runs are free.
Quorum maps the agent’s capabilities to each tool’s
auto-approval: an agent with the code capability runs with the tool’s
write/auto-approve flag; without it, read-only.
Direct HTTP — any OpenAI-compatible server
Not every brain needs a binary. The openai provider speaks the
OpenAI-compatible /v1/chat/completions protocol directly over HTTP — no CLI,
no SDK — so one type covers a remote OpenAI or
OpenRouter endpoint and every server that exposes the
same API locally: LM Studio,
vLLM, llama.cpp, and Ollama’s own OpenAI endpoint.
### Reviewer # LM Studio on this machine — free
- provider: { "type": "openai", "baseUrl": "http://localhost:1234/v1", "model": "qwen3-30b" }
### Router # a vLLM box on the LAN
- provider: { "type": "openai", "baseUrl": "http://10.0.0.5:8000/v1", "model": "meta-llama/Llama-3.3-70B-Instruct" }
### Judge # OpenRouter
- provider: { "type": "openai", "baseUrl": "https://openrouter.ai/api/v1", "apiKeyEnv": "OPENROUTER_API_KEY", "model": "anthropic/claude-sonnet-5" }
Three keys configure it:
model(required) — the id the server exposes, i.e. what its/v1/modelslists.baseUrl— the server’s API root. Defaulthttps://api.openai.com/v1; LM Studio serves athttp://localhost:1234/v1, vLLM athttp://localhost:8000/v1out of the box.apiKeyEnv— the name of the env var holding the bearer token (defaultOPENAI_API_KEY), read at call time from the environment Quorum runs in; a stored secret of that env-var name (e.g.openai-api-key) also reaches a granted agent’s task turns. With no key set, no Authorization header is sent — exactly what a local server wants.
Like llm, this is a text brain — no tool loop: on a task it deliberates
and answers, but never runs Bash or edits files. That makes it the cheap,
non-agentic pick for reviewers, judges, routers, and meeting chatter — where a
full agent binary is overkill — and a natural fallbacks entry. Token usage
from each response is metered to the ledger; supply
pricing for a USD cost, or let local runs meter free. And because a plain
completion has no side effects, transient failures (a rate-limit, a server
blip) are retried safely — something the tool-running brains deliberately never
do.
The fake brain
The fake provider (config { "type": "fake" }) is a deterministic, no-API
stub for tests and offline dry-runs. It never emits the org’s control markers
(a JSON plan, SCORE: n, RESOLVED: yes), so control flows fall back
predictably. Give one agent — or defaults.provider — this type to exercise a
whole QUORUM.md without spending a token or needing the binary.
Configuring a brain
A provider is configured per agent (overriding the org default) via the
provider object. Every field:
| field | meaning |
|---|---|
type |
"claude", "codex", "opencode", "cline", "copilot", "cursor", "grok", "qwen", "amp", "pi", "goose", "hermes", "kimi", "droid", "antigravity", "dsh", "llm", "openai", or "fake" |
model |
model id, e.g. claude-opus-5, gpt-5-codex, gpt-oss:20b. Leave it unset and the tool runs the model it’s configured with (required for openai — what the server’s /v1/models lists) |
effort |
reasoning effort: low | medium | high | xhigh | max (claude) |
maxBudgetUsd |
hard per-session spend ceiling enforced by the binary (claude) |
bin |
path to the binary (default $CLAUDE_BIN / $CODEX_BIN or the binary name) |
baseUrl |
API root of an OpenAI-compatible server, e.g. http://localhost:1234/v1 for LM Studio (openai; default https://api.openai.com/v1) |
apiKeyEnv |
name of the env var holding the bearer token; unset means no auth header (openai; default OPENAI_API_KEY) |
timeoutMs |
per-call timeout (default 10 minutes) |
retries |
retry count on failure (default 3) |
fallbacks |
failover chain — provider objects tried in order on a rate-limit, auth, or upstream failure; each entry inherits this config and overrides what it sets — see Failover |
args |
extra raw CLI args appended to every call |
launch |
launcher prefix, e.g. "launch claude -y --model gemma4 --" (claude) |
oss |
run against a local open-source model (codex) |
localProvider |
ollama | lmstudio — which local server oss targets (codex) |
sandbox |
read-only | workspace-write | danger-full-access (codex; else derived from tools) |
mcp |
path(s) to MCP server config JSON the org owns; null disables (claude) |
browser |
give this brain the org-owned browser — true, or { "headless": bool, "allowedOrigins": [...], "blockedOrigins": [...] } — see The browser (claude) |
strictMcp |
isolate from the operator’s own MCP servers (default true) (claude) |
settingSources |
--setting-sources value, e.g. "project,local" (claude) |
bare |
run lean and isolated: skip the machine’s bootstrapping (hooks, plugins, CLAUDE.md discovery) and leave no resumable session (default false) (claude/codex/hermes) |
pricing |
cost-per-token override for the ledger — { "inputPerMTok": n, "outputPerMTok": n } (Codex and openai need this for a USD cost) |
Per-agent brains
Any agent — seeded or hired — may run on its own brain. This is one of the most useful levers in Quorum: put a top model on the roles that need judgment, and a cheaper one at lower effort on routine execution.
An agent’s provider replaces the org default wholesale — it isn’t merged
field by field — so an agent that only wants a different model must restate
the rest of the config it needs (like the org’s mcp path).
### CTO
- provider: { "type": "claude", "model": "claude-opus-5", "effort": "high" }
### Engineer
- provider: { "type": "claude", "model": "claude-sonnet-5", "effort": "medium", "maxBudgetUsd": 2 }
A hired agent’s brain is persisted on the roster, so it survives restart. And the hiring directive can choose the brain at hire time — see Hiring & delegation:
HIRE[Engineer | model=claude-sonnet-5 effort=low]: build and verify the API
Failover
An org runs unattended for days, so one vendor’s bad hour must not stall it. A
provider’s fallbacks list is its failover chain: when a call fails on a
recoverable class — a rate-limit or exhausted quota, a rejected credential,
an upstream outage — the same call is re-issued against the next entry. Each
entry inherits the primary’s full config (MCP tools, timeouts, binary) and
overrides only what it sets:
- defaults: { "provider": { "type": "claude", "model": "claude-opus-5", "fallbacks": [{ "model": "claude-sonnet-5" }, { "type": "codex" }] } }
Here a rate-limited Opus call degrades to Sonnet, and if the whole vendor is
down, to the Codex CLI — the org keeps moving instead of stalling. The chain is
one flat list: a fallbacks inside a fallback entry is ignored. What
failover deliberately does not do:
- A refusal or a deterministic error fails fast. If the model declined the prompt or the turn crashed on its own logic, a different brain would burn spend on the same problem — the failure surfaces normally instead.
- Fallback turns never mint resumable sessions. Session continuity stays keyed to the primary brain; a failover turn runs fresh, and the task resumes its primary session once the primary recovers.
Every substitution is recorded in the audit log as a
provider.failover event — who downgraded, from what, to what, and why — and
the cost ledger attributes each call to the brain
that actually served it, so a degraded hour is visible, not silent.
External services & MCP
External services — a shared Google Workspace account for invites and email, a
Slack, a Grafana, an internal API — reach the agents through MCP servers the
org owns. Point mcp at a config the org controls (org-wide via
defaults.provider, or on a single agent’s provider) and each server it
declares becomes a capability of the same name, gated per agent through the
same allow / ask / deny policy as every other tool.
Attaching a server is the opt-in — it’s allow by default, and you withhold it
from an agent with deny (or ask).
- defaults: { "provider": { "type": "claude", "mcp": "./integrations.mcp.json" } }
### Ops
- capabilities: { "gworkspace": "allow" }
### Analyst
- capabilities: { "gworkspace": "deny" }
integrations.mcp.json is a standard MCP config (the mcpServers shape the
binary reads); its keys are the capability names. Credentials never go in
QUORUM.md — they live in the MCP server’s own config or environment, where the
server holds the account and does the auth. A relative mcp path resolves
against the org home, so a file beside QUORUM.md is found wherever a brain runs.
By default (strictMcp: true) an agent is also isolated from the operator’s own
MCP servers, so an org only ever reaches the servers it wired here — never your
personal tool configuration. Set mcp to null to disable, or strictMcp: false to deliberately inherit the host’s. settingSources similarly limits
which settings files the binary loads. For the strongest isolation, set bare: true — the brain then skips the machine’s bootstrapping entirely (hooks,
plugins, CLAUDE.md discovery, keychain reads) and leaves no resumable session
file behind: every turn is a clean headless one-shot fed only by the org, which
also opts the agent out of same-task session continuity. Under
bare the Claude binary authenticates strictly from ANTHROPIC_API_KEY, so keep
it off if your agents log in with a subscription instead.
For a plain CLI — a grafana binary, gh, kubectl, curl against an API — no
MCP is needed: the code capability’s Bash tool runs any command installed on
the machine. Reach for an MCP server when you want a structured, per-service
grant an operator can allow or deny on its own; Bash is all-or-nothing.
The browser
Some of the world has no API: signup flows, web portals, dashboards behind a login, forms. One provider key gives an agent a real browser for exactly that work:
- defaults: { "provider": { "type": "claude", "browser": true } }
### Analyst
- capabilities: { "browser": "deny" } # everyone but the analyst may browse
The browser is just one more org-owned MCP server — Playwright MCP, spawned via
npx like any host CLI — named browser, so everything above applies: it’s
gated per agent as the browser capability (wiring it is the opt-in;
ask/deny carve out exceptions), and strictMcp isolation still holds. An
allowed agent gets tools to navigate, click, type, fill forms, read pages, and
take screenshots — and is told it has them.
Two defaults matter:
- Headless. An autonomous org runs unattended; no window opens. Set
"browser": { "headless": false }to watch it work — or to complete a CAPTCHA or 2FA login step by hand in the window the agent opened. - A persistent per-agent profile under the org home (
browser/<agent-id>, kept out of version control — cookies are credentials). Logins persist across sessions, so an account the org opened stays usable, and your own browser profile is never touched.
"allowedOrigins" / "blockedOrigins" (lists of origins) fence where it may
go. For anything beyond these knobs — a different browser engine, viewport,
tracing — wire Playwright MCP yourself through mcp (the shorthand expands to
exactly that config) and the name you give the server becomes its capability.
Requires npx (ships with Node) and a Chrome on the machine; the first use
downloads the MCP package.