All docs
docs / Build on it
Adapter Spec
The AgentAdapter interface and every connector family.
On this page
- 1. Interface
- 2. Adapter families
- 2.1 openai-compatible (route: api) — one adapter, many vendors
- 2.2 anthropic (route: api) and gemini (route: api)
- 2.3 Coding CLIs (route: cli) — run official binaries headless inside the sandbox
- 2.4 Hosted agent APIs (route: hosted) — vendor runs the sandbox
- 2.5 A2A (route: a2a)
- 2.6 MCP (route: mcp)
- 2.7 Browser / computer use (route: browser via sdk/hosted)
- 2.8 Media (route: api)
- 2.9 Research tools (route: api/mcp)
- 3. Explicitly unsupported (adapters must not exist)
- 4. Adapter test kit
- 5. Adding a new connector (checklist)
Every connector is reached through one interface. Vendor quirks live only inside packages/adapters/<family>/.
1. Interface
// packages/adapters/src/types.ts
import type { TaskContract, HandoffPacket, MemoryBrief, AgentConfig, BrioEvent, Artifact } from "@brio/schema";
export type Route = "api" | "cli" | "sdk" | "hosted" | "a2a" | "mcp" | "browser" | "local";
export interface AdapterCapabilities {
taskTypes: string[]; // from the taxonomy in ROUTING_AND_QUALITY.md
editsFiles: boolean; // works on a repo/worktree
runsCode: boolean; // executes commands itself
browses: boolean;
producesMedia: ("image" | "video" | "audio")[];
streaming: boolean;
supportsMcpTools: boolean; // can be given Brio mailbox + other MCP servers
supportsResume: boolean;
maxContextTokens?: number;
runnerModes: ("cloud" | "local")[];
}
export interface RunInput {
attemptId: string;
taskTitle: string;
instructions: string; // rendered prompt (role + task + contract + constraints)
contract: TaskContract;
handoffs: HandoffPacket[];
brief: MemoryBrief;
workdir?: string; // worktree path inside sandbox (file-editing agents)
attachments: Artifact[];
limits: { maxCostUsd: number; maxMinutes: number; maxOutputTokens?: number };
mcpServers: McpServerConfig[]; // always includes brio-mailbox for capable agents
}
export interface RunContext {
credential: SecretHandle; // resolved just-in-time from vault; never logged
emit(e: AdapterEvent): void; // streamed to orchestrator → BrioEvent
signal: AbortSignal; // cancel / limit breach
sandbox: SandboxHandle; // exec, files, network policy
logger: Logger; // redacting logger
fetch: FetchLike; // outbound HTTP, behind the URL guard (D-32); fixtures replay through it
tools?: { search?: SearchProvider; mailbox?: Mailbox }; // offered to API models only when present
}
export type AdapterEvent =
| { kind: "message"; role: "assistant" | "tool"; text: string }
| { kind: "tool_call"; name: string; args: unknown }
| { kind: "file_edit"; path: string; op: "create" | "modify" | "delete" }
| { kind: "cost"; usd: number; tokensIn?: number; tokensOut?: number }
| { kind: "progress"; note: string }
| { kind: "needs_input"; question: string }; // routed to mailbox / user
export interface RunResult {
status: "completed" | "failed" | "cancelled" | "limit_exceeded";
summary: string; // agent's own summary, goes into handoff packet
artifacts: Artifact[]; // diffs are collected by runner from worktree; adapters add others
usage: { usd: number; tokensIn: number; tokensOut: number; seconds: number };
output: string; // the agent's text answer (stored as a report artifact)
tokens: TokenUsage; // input / cached / cache-write / output / reasoning, summed over calls
error?: string; // why it ended, when not completed
vendorRunId?: string; // for resume / audit
raw?: unknown; // stored, never sent to other vendors
}
export interface AgentAdapter {
key: string; // matches registry id, e.g. "cursor-agent"
route: Route;
capabilities(config: AgentConfig): AdapterCapabilities;
healthCheck(connector: ConnectorConfig, ctx: Pick<RunContext, "credential" | "logger" | "fetch">): Promise<HealthStatus>;
listModels?(connector: ConnectorConfig, ctx: Pick<RunContext, "credential" | "fetch">): Promise<ModelInfo[]>;
estimate(input: RunInput, config: AgentConfig): Promise<{ usd: number; minutes: number }>;
run(input: RunInput, config: AgentConfig, ctx: RunContext): Promise<RunResult>;
resume?(vendorRunId: string, message: string, ctx: RunContext): Promise<RunResult>;
}ConnectorConfig is the registry preset (D-27) plus the installed connector's base URL and API style. Health checks never spend money (D-31). The implementation is packages/adapters/src/types.ts.
Registration: packages/adapters/src/index.ts exports a map key → AgentAdapter. packages/registry validates that every P0/P1 registry entry with an implemented route has an adapter, and fails CI otherwise.
2. Adapter families
2.1 openai-compatible (route: api) — one adapter, many vendors
Covers any endpoint speaking OpenAI Chat Completions / Responses: OpenAI, xAI Grok (change base URL), Meta Model API (muse-spark-1.1), DeepSeek, Kimi, GLM, Qwen, Mistral (compat mode), Sarvam, OpenRouter, Together, Fireworks, Groq, Cerebras, LiteLLM proxy, Ollama/vLLM/LM Studio, Vercel/Cloudflare gateways.
- Config:
baseUrl,apiStyle("responses" | "chat"),model,headers,supportsTools,supportsReasoningParam. - Implements an internal tool loop when the task needs tools (file read/write inside sandbox, search, Brio mailbox), so pure models can still act as agents.
- Pricing from a
pricingtable refreshed from provider docs/OpenRouter; cost event per response.
2.2 anthropic (route: api) and gemini (route: api)
Native APIs (called with fetch, D-28) for features compat layers lose: prompt caching, extended thinking, computer-use tool (Anthropic), long context and Interactions API (Gemini). Same internal tool loop as 2.1.
2.3 Coding CLIs (route: cli) — run official binaries headless inside the sandbox
All share a CliAdapterBase: spawn process in workdir, parse NDJSON/stream-json stdout into AdapterEvents, enforce timeout, capture exit code, inject MCP config file, pass credentials via env only.
Every flag below was checked against the CLI's own --help inside the sandbox image on 23 Oct 2026 (versions in D-36). Implementations: packages/adapters/src/cli/.
| Adapter key | Headless invocation (as implemented) | Key (env) | Notes |
|---|---|---|---|
claude-code |
claude -p --output-format stream-json --verbose --permission-mode bypassPermissions --model <m> --max-turns <n> --max-budget-usd <usd> --setting-sources "" --strict-mcp-config --no-session-persistence (prompt on stdin) |
ANTHROPIC_API_KEY |
Reports cost. is_error, not subtype, signals failure. Bypass mode refuses root (sandboxes are non-root). D-42 |
codex |
codex exec --json --skip-git-repo-check --ephemeral --dangerously-bypass-approvals-and-sandbox --model <m> --cd /work -c check_for_update_on_startup=false -c analytics.enabled=false <prompt> |
CODEX_API_KEY |
Tokens per turn, no cost (priced from the OpenAI table). Only turn.failed is fatal. Needs ca-certificates |
cursor-agent |
cursor-agent -p --output-format stream-json --force --trust --approve-mcps --sandbox disabled --workspace /work --model <m> <prompt> |
CURSOR_API_KEY |
-p --trust or it hangs; --force or it only proposes. No usage or cost: unmetered (D-37). Commit attribution turned off |
antigravity |
agy -p <prompt> --output-format stream-json --dangerously-skip-permissions --model <m> --print-timeout <n>m --add-dir /work |
GEMINI_API_KEY + settings {"modelProvider":"gemini"} |
Exit status untrusted (issue 548): the result event, diff and checks decide. API-key mode, so it also runs in cloud sandboxes; a user's signed-in agy is for the local runner only (M8), and its OAuth token is never copied |
opencode |
opencode run --format json --auto --pure --model openrouter/<m> --dir /work <prompt> |
OPENROUTER_API_KEY |
Cost per step |
aider |
aider --model anthropic/<m> --message <prompt> --yes-always --no-auto-commits --no-attribute-author … |
ANTHROPIC_API_KEY |
No structured output: text lines, its session-cost line, and the runner's diff |
goose |
goose run --no-session --quiet --output-format stream-json --provider anthropic --model <m> --max-turns <n> --no-profile --with-builtin developer --text <prompt> |
ANTHROPIC_API_KEY |
Repo moved to aaif-goose/goose; cost in the complete event |
copilot-cli, grok-build, kiro-cli, qwen-code, kimi-cli, cline-cli, amp, droid, junie, warp |
each vendor's headless flag | vendor key | Later (P1/P2); same base |
Prompts longer than 100 KB go to the agent as .brio/prompt.md (one argv element is capped at 128 KiB). Keys reach the CLI only through a 0600 env file read by docker exec, never argv. Each CLI's vendor host is its only egress besides package registries and git (D-39).
Under the person's own sign-in (local runner, M8, D-87). A connector installed with localLogin (only where the registry's localLogin says the vendor allows it) holds no key; its agents run on their owner's local runner with options.localLogin = true. The base then puts no key in the environment, uses the spec's localArgv when it has one (Antigravity: plain agy -p …, without the wrapper that writes key-mode settings into $HOME), and meters nothing (the vendor bills the person's plan; tokens are still reported). In host mode /work in argv and paths means the attempt's worktree.
Lease enforcement for CLIs: generate the agent's own hook config (Claude Code .claude/settings.json PreToolUse, Cursor .cursor/hooks.json, Codex .codex/hooks.json, Antigravity .agents/hooks.json) that calls the local runner's lease endpoint before writes — the same mechanism Relay uses, but checking task leases and contracts.
2.4 Hosted agent APIs (route: hosted) — vendor runs the sandbox
Pattern: create session/task → stream or poll events → fetch outputs (diff, PR URL, files) → map to artifacts. As implemented (M6): every service connector is registry data (services in registry/connectors.json: base URL with settings, auth scheme incl. SigV4 and Entra, a free key check, the billing unit, the task types it takes; D-62), and every vendor is one JobVendor (start, poll, answer, cancel, outputs) on the shared job runner in packages/adapters/src/services/job.ts. Questions and approvals go to a person through the mailbox (10 minutes; no answer is no; D-64). Changes come back as a patch applied to the task's own worktree. Wire facts per vendor: docs/vendors/hosted-agents.md.
| Adapter key | API (verified 23 Oct 2026) | Notes |
|---|---|---|
claude-managed-agents |
Claude Platform REST, beta header managed-agents-2026-04-01 |
Built-in tools: files, shell, web, code. Not ZDR/HIPAA eligible yet → policy may block for sensitive projects. |
openai-agents |
POST /v1/agents/sessions, header OpenAI-Beta: agents=v1; events incl. agent.session.requires_action |
Hosted browser (since 29 Sep 2026): Brio must answer site-access and sign-in approval requests → route to approvals. |
gemini-managed-agents |
Gemini Interactions API (preview) | Persistent sandboxes, saved agents, triggers, budgets, remote MCP. |
jules |
Jules REST API v1alpha (sessions, activities, approve plan, messages, outputs) | GitHub repos only; output is a PR → treat as diff artifact. |
devin |
Devin API (sessions) | Output PRs/messages. |
manus |
Manus API (tasks, projects, connectors, webhooks) | Polled; with a public https Brio, a webhook (/v1/hooks/manus/:connectorId, RSA-signed) adds a board note at once (D-72). |
bedrock-agentcore |
AgentCore Runtime invoke + managed harness | For AWS-hosted customer agents. |
foundry-agents |
Foundry Agent Service (Responses API format) | For Microsoft customers. |
codex-cloud |
Codex Cloud tasks | ChatGPT-plan auth → local runner, user-initiated only; verify allowed programmatic access before shipping. |
2.5 A2A (route: a2a)
- Discover: fetch Agent Card (
/.well-known/agent-card.json), store skills, auth scheme, streaming support. - Map skills → task types (planner-assisted mapping stored on the connector; user can edit).
- Send
message/sendor streamingmessage/stream; track A2A task id; map parts/artifacts to Brio artifacts; support input-required state → mailbox/approvals. - Use the official A2A JS SDK; protocol v1.0 (Mar 2026).
- As implemented (M7): registry service
a2a(URL setting, optional bearer); the card is read at install, its skills suggest task types (editable), each agent is its own vendor by host, runs are unmetered (D-79).
2.6 MCP (route: mcp)
MCP servers are tools, not agents. Attach them to agents via RunInput.mcpServers. Brio ships its own MCP servers:
brio-mailbox(ask_agent, inbox, answer, report_blocker, request_review, get_contract, get_brief, report_progress) — given to every capable agent: in-process for model-API agents (new messages arrive between turns), and as a streamable-HTTP MCP server for coding CLIs, reached through the sandbox's proxy atbrio-mailbox:8080with a per-attempt token (D-43). Claude Code, Codex, Cursor and OpenCode are configured for it; Antigravity, Goose and Aider run without it for now.brio-lease(claim, release, status) — used by hooks and agents that prefer explicit claims.- As implemented (M7): any remote MCP server installs as registry service
mcpand attaches to agents as a tool (D-80); model-API agents call its tools in their tool loop; coding CLIs get it in their MCP configuration.
2.7 Browser / computer use (route: browser via sdk/hosted)
anthropic-computer-use(Messages API tool, our sandboxed browser VM),openai-agentshosted browser,nova-act(SDK),browser-use(library or cloud API), cloud browsers (Browserbase/Steel) as infrastructure.- Always behind
browsersandbox profile with per-task URL allowlist; credentials for sites never typed by agents — use approvals + user-side sign-in handoff. - As implemented (M6):
anthropic-computer-usedrives Chromium on a virtual display in thesandbox-browserimage with xdotool and screenshots; the allowlist is the hosts the contract names (D-69). Withoptions.browser = "cloud"it drives a Browserbase or Steel session over CDP instead, the allowlist enforced in the browser (D-70).browser-useruns Browser Use Cloud v4, capped at the attempt's budget (D-71).
2.8 Media (route: api)
openai-image, openai-speech, google-media (Gemini Omni video and Nano Banana images through the Interactions API; Veo left the Gemini API on 22 Oct 2026), flux, runway, luma, kling, elevenlabs, ideogram, recraft. Async job pattern (submit → poll/webhook → download → artifact with spec metadata: size, duration, format). Verifier runs spec checks and an optional vision-model review. As implemented (M6): the size, aspect, duration and format asked for come from the task's media_spec check; results are downloaded at once and placed at the contract's output path; voice reads a script file the contract names, never the instructions; metering in the vendor's unit (D-65). Wire facts: docs/vendors/media.md.
2.9 Research tools (route: api/mcp)
Search APIs (Exa, Tavily, Parallel, Brave), Perplexity Agent API, OpenAI search models, Grok live search, page fetchers. Exposed to agents as MCP tools from packages/adapters/research so every agent gets the same search surface (makes results comparable for judging). As implemented (M6): search and fetch services are workspace tools, not agents — web_search and fetch_url in-process for model APIs and on the brio-mailbox MCP server for coding CLIs, metered per call, never on confidential tasks (D-66). Perplexity's Agent API, OpenAI's Responses web search and xAI's web and X search are research agents that answer with a numbered source list; Gemini Deep Research runs as a managed agent. Wire facts: docs/vendors/research-browser.md.
3. Explicitly unsupported (adapters must not exist)
Midjourney (no official API), Suno/Udio wrappers, Perplexity Comet driving, ChatGPT/Claude/Gemini/Grok consumer web apps, Genspark/Kimi/MiniMax consumer agents, any adapter that reads another app's stored tokens. packages/registry marks these route: none and the UI shows "not supported — here's the legitimate alternative".
4. Adapter test kit
Each adapter ships:
- Unit tests for event parsing using recorded fixtures (
__fixtures__/*.ndjson). - A contract test suite (
packages/adapters/test/contract.ts) every adapter must pass: health check, estimate, run "hello task" in sandbox, cancel mid-run, limit breach, credential redaction (assert secret never appears in events/logs). - Live tests gated by env vars (
LIVE_<KEY>=1) run nightly, not in PR CI.
5. Adding a new connector (checklist)
- Add/verify entry in
registry/connectors.json(route, auth, docs URL, verified date). - Pick family base class; implement
AgentAdapter. - Record fixtures; pass contract suite.
- Add pricing entries; add to router candidate pool with prior scores (Beta(1,1)).
- Add setup steps to the connector page copy.