Adapters

Bernstein is model-agnostic. Use Aider, Amp, Claude Code, Codex, Cursor, Gemini CLI, Qwen, Roo Code — or any CLI agent — without changing your orchestration. Switch providers without rewriting anything.

Why model-agnostic

Every major AI lab now ships an agent framework that works best (or only) with their own models. That creates fragility: today's best model is rarely tomorrow's, and enterprise teams can't afford to rewrite orchestration every time they evaluate a new provider.

Bernstein decouples orchestration from model choice. The orchestrator is deterministic Python — it schedules tasks, routes models, and verifies results without talking to any LLM. Each CLI agent is a thin subprocess adapter. Swapping the adapter is a one-line config change.

Concrete benefits:

Feature matrix

Feature Aider Amp Claude Code Codex CLI Cursor Gemini CLI Kilo Qwen Roo Code Generic
Model selection Any (via --model) opus 4.6 / gpt-5.4 opus / sonnet / haiku gpt-5.4 / o3 / o4-mini sonnet 4.6 / opus 4.6 / gpt-5.4 gemini-3-pro / 3-flash / 2.5-pro Any (provider/model) qwen3-coder / qwen-max opus 4.6 / sonnet 4.6 / gpt-4o Pass-through
Effort levels max / high / medium / low
MCP config injection ✓ (--add-mcp) ✓ (--mcp)
Structured JSON output ✓ (stream-json)
Tier detection ✓ (API key prefix) ✓ (org ID / key prefix) ✓ (~/.cursor/ present) ✓ (GCP project / key format) ✓ (~/.kilo/ or API key) ✓ (provider env vars)
Auto-approval --yes Built-in --dangerously-skip-permissions --approval-mode full-auto Built-in agent mode --sandbox none --yes -y Via config Via extra_args
Web search Depends on model Native Via MCP Native Native Native Depends on model Via Tavily (optional) Via MCP Depends on CLI
OpenAI-compatible backends ✓ (OpenRouter, Together, Oxen)
Local / offline models ✓ (via OpenAI-compat server) ✓ (via OpenAI-compatible server)

Aider

Provider: OpenAI / Anthropic / any OpenAI-compatible  |  CLI flag: --cli aider

pip install aider-chat
export OPENAI_API_KEY=sk-...   # or ANTHROPIC_API_KEY

Aider is a popular open-source coding assistant that works with any model provider. Bernstein runs it with --yes --no-git --message <prompt> for non-interactive headless operation. Model selection is passed through via --model. Aider supports local models through any OpenAI-compatible API server (LM Studio, Ollama, vLLM).

Amp

Provider: Sourcegraph  |  CLI flag: --cli amp

brew install amp
# Auth via Sourcegraph account

Amp is Sourcegraph's coding agent. Bernstein uses its headless mode for autonomous operation. Model selection depends on the user's Sourcegraph subscription tier.

Claude Code

Provider: Anthropic  |  CLI flag: --cli claude

npm install -g @anthropic-ai/claude-code
export ANTHROPIC_API_KEY=sk-ant-...

The Claude adapter has the most integration depth: it uses --output-format stream-json to parse structured output, maps effort levels to --effort and --max-turns, and passes MCP server config directly via --mcp-config.

Model mapping:

Bernstein tierClaude model
opusclaude-opus-4-6
sonnetclaude-sonnet-4-6
haikuclaude-haiku-4-5-20251001

Codex CLI

Provider: OpenAI  |  CLI flag: --cli codex

npm install -g @openai/codex
export OPENAI_API_KEY=sk-proj-...

Runs in --approval-mode full-auto with --quiet to suppress interactive prompts. Pass any OpenAI model name directly via --model. Recommended model: gpt-5.4 (Mar 2026). The o-series reasoning models (o3, o4-mini) are also supported. Tier detection checks for OPENAI_ORG_ID (enterprise) and key prefix (sk-proj = pro).

Cursor

Provider: Cursor AI  |  CLI flag: --cli cursor

# Install Cursor from https://www.cursor.com
# Sign in via the Cursor app — auth is stored in ~/.cursor/
cursor agent --version

Cursor is a VS Code fork with built-in AI. The cursor agent subcommand runs the agent headlessly in the terminal. Bernstein uses --user-data-dir to isolate each session's state, and --add-mcp to inject MCP server config. The adapter does not pass --model because Cursor selects the model from the user's subscription settings.

Authentication is via the Cursor app OAuth session — no separate API key is needed. Bernstein detects login by checking for ~/.cursor/. Tier detection reports Pro when logged in.

Gemini CLI

Provider: Google  |  CLI flag: --cli gemini

npm install -g @google/gemini-cli
export GOOGLE_API_KEY=AIza...

Runs with --sandbox none. The default model is gemini-3-pro (preview, Mar 2026). Gemini 2.5 Pro/Flash are GA but deprecating June 2026. For Vertex AI / enterprise use, set GOOGLE_CLOUD_PROJECT — tier detection will pick this up and apply higher rate limits accordingly.

Kilo

Provider: Stackblitz (multi-provider)  |  CLI flag: --cli kilo

# Install from https://kilocode.ai
export KILO_API_KEY=...
kilo --version

Kilo is a multi-provider agent CLI from Stackblitz. It supports headless mode, ACP/MCP protocols, and session management. Bernstein invokes it as kilo run --prompt "<task>" --model <provider/model> --yes. Pass any provider/model string — e.g. anthropic/claude-sonnet-4-6, openai/gpt-5.4, or google/gemini-2.5-pro.

Authentication is via KILO_API_KEY (preferred) or OAuth session stored in ~/.kilo/. MCP server config is injected via --mcp. Tier detection reports Pro when any auth is found.

Qwen

Provider: Alibaba / OpenRouter / any OpenAI-compatible  |  CLI flag: --cli qwen

npm install -g qwen-code

The Qwen adapter is the most flexible: it routes to different backends depending on which API keys are set. Priority order:

BackendEnv varUse case
OpenRouter (paid)OPENROUTER_API_KEY_PAIDBest quality via OpenRouter
OpenRouter (free)OPENROUTER_API_KEY_FREEFree-tier routing, rate-limited
Together.aiTOGETHERAI_USER_KEYOpen-source models, fast inference
OxenOXEN_API_KEYEnterprise open-source
Default (native Qwen)DASHSCOPE_API_KEYAlibaba Cloud, qwen-max / coder

This makes Qwen a good choice for cost-sensitive runs: route simple tasks to OpenRouter free-tier, keep expensive capacity for complex ones.

Roo Code

Provider: Anthropic / OpenAI / any  |  CLI flag: --cli roo-code

# Install Roo Code VS Code extension from https://github.com/RooVetGit/Roo-Code
# Headless CLI mode available via the extension's CLI interface

Roo Code is a VS Code extension with a headless CLI mode. It supports multiple providers (Anthropic, OpenAI, and OpenAI-compatible backends) and can use local models. Bernstein spawns it in headless mode for autonomous task execution.

Generic adapter

CLI flag: --cli generic

The generic adapter wraps any CLI agent without writing Python. Configure it via bernstein.yaml:

cli: generic
cli_options:
  command: aider        # the binary name
  prompt_flag: --message
  model_flag: --model
  extra_args:
    - --yes
    - --no-git

Bernstein calls: aider --model <model> --yes --no-git --message <prompt>. Any coding agent with a non-interactive mode works.

OpenClaw experimental

Type: Runtime bridge (not a CLI adapter)  |  Status: Stub ready, awaiting OpenClaw API GA

OpenClaw provides ephemeral, network-isolated cloud sandboxes for running CLI coding agents remotely. Instead of spawning agents on your local machine, Bernstein can delegate execution to OpenClaw containers — useful for CI/CD, resource-constrained machines, or security-sensitive runs.

Configuration in bernstein.yaml:

bridges:
  openclaw:
    endpoint: https://api.openclaw.dev/v1
    region: us-east-1
    sandbox_class: large

Note: The OpenClaw bridge is currently a stub (NotImplementedError). It will be fully wired when the OpenClaw API reaches general availability. Track progress in the project roadmap.

Add a custom adapter

If you need deeper integration than the generic adapter provides (structured output parsing, custom tier detection, etc.), implement the CLIAdapter interface:

from bernstein.adapters.base import CLIAdapter, SpawnResult
from bernstein.core.models import ModelConfig
from pathlib import Path
from typing import Any

class MyAgentAdapter(CLIAdapter):
    def spawn(
        self,
        *,
        prompt: str,
        workdir: Path,
        model_config: ModelConfig,
        session_id: str,
        mcp_config: dict[str, Any] | None = None,
    ) -> SpawnResult:
        log_path = workdir / ".sdd" / "runtime" / f"{session_id}.log"
        log_path.parent.mkdir(parents=True, exist_ok=True)

        cmd = ["my-agent", "--model", model_config.model, "-p", prompt]

        import subprocess
        with log_path.open("w") as log_file:
            proc = subprocess.Popen(
                cmd, cwd=workdir,
                stdout=log_file, stderr=subprocess.STDOUT,
                start_new_session=True,
            )

        return SpawnResult(pid=proc.pid, log_path=log_path)

    def name(self) -> str:
        return "My Agent"

Register it in your project:

# bernstein_plugins.py (auto-discovered at startup)
from bernstein.adapters.registry import register_adapter
from my_package.adapter import MyAgentAdapter

register_adapter("my-agent", MyAgentAdapter())

Then use it: bernstein run --cli my-agent.

Mixing adapters in one run

Different task roles can use different adapters. Configure per-role overrides in bernstein.yaml:

goal: "Add auth, write tests, and document the API"

# Default adapter for all roles (auto-detects installed agents)
cli: auto

# Per-role overrides
role_config:
  qa:
    cli: codex          # GPT-5.4 for test generation
    model: gpt-5.4
  docs:
    cli: gemini         # Gemini Flash for docs — cheaper
    model: gemini-3-flash

The orchestrator assigns each task to the right adapter based on role. All tasks write to the same .sdd/ state — adapter choice is transparent to the rest of the system.

See the multi-model example for a working demo with three adapters in a single run.