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 code capability runs workspace-write (it can edit files and run commands in its folder); without it, read-only. Override per agent with sandbox.
  • Tokens, priced by you. Codex reports token usage but not a dollar figure. Supply a pricing rate 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-model plugin (it defaults to deepseek-v4-flash on DeepSeek’s own API, with DEEPSEEK_API_KEY from the environment). A provider.model is therefore ignored unless a launcher owns it; to change it directly, pass a config overlay in args: { "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 dsh at 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:

  • grok reaches xAI’s Grok models — auth via an interactive grok login or GROK_CODE_XAI_API_KEY from the xAI console. Pick a model id from grok models, or leave model unset for your account default.
  • qwen has a free tier: run qwen once interactively to complete the OAuth login, then the org can use it headlessly. Reports real token counts.
  • amp has no model ids — its --mode tiers (low, medium, high, ultra) pick the model and harness, so Quorum’s model field maps onto them: { "type": "amp", "model": "high" }. Auth via amp login or AMP_API_KEY.
  • pi takes model as "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.
  • goose takes its provider from your goose config or GOOSE_PROVIDER; pair model with args: ["--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/models lists.
  • baseUrl — the server’s API root. Default https://api.openai.com/v1; LM Studio serves at http://localhost:1234/v1, vLLM at http://localhost:8000/v1 out of the box.
  • apiKeyEnv — the name of the env var holding the bearer token (default OPENAI_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.