Skip to content
Integrations
Integrations

AForge — the default harness

AForge is AgentField's native coding harness. It ships with af, needs no CLI install, and runs every app.harness(...) call that does not name a provider.

AForge is AgentField's native coding harness, and it is the default. Every app.harness(...) call that does not name a provider runs on AForge — no CLI to install, no per-worker coding-agent account to create, no provider= argument to remember (just one OPENROUTER_API_KEY).

This page is the canonical reference for the default path. If you would rather drive Claude Code, Codex, Gemini CLI, or OpenCode instead, that is one field — see Choose a different worker below.

Nothing to install

AForge is provisioned alongside the af binary, so if you have AgentField you already have it:

How you got AgentFieldHow you got AForge
curl -sSf https://agentfield.ai/get | shInstalled by the same script.
AgentField DesktopDownloaded on launch — the app runs af aforge ensure.
Official Docker imagesBaked in at build time by the python-agent, go-agent, and cloud control-plane images. The plain control-plane image does not carry it.

The one thing you supply is a key:

export OPENROUTER_API_KEY=sk-or-...

That is the entire setup. The first harness call works from there.

from pydantic import BaseModel
from agentfield import Agent

class ReviewResult(BaseModel):
    findings: list[str]
    severity: str  # "low" | "medium" | "high"

app = Agent(node_id="reviewer")

@app.reasoner()
async def review_diff(diff: str) -> dict:
    # No provider, no model — this runs on AForge.
    result = await app.harness(
        f"Review this diff. Be precise, no fluff.\n\n{diff}",
        schema=ReviewResult,
    )
    if result.is_error:
        return {"ok": False, "error": result.error_message}
    return {"ok": True, "review": result.parsed.model_dump()}

Configuration

SettingWhereDefaultWhat it does
OPENROUTER_API_KEYenvRequired. AForge's model access.
AFORGE_MODELenvAForge's own defaultOverrides the model AForge runs. Leave it unset unless you have a reason.
AFORGE_EXEC_BUDGETenv150000 tokensAForge's own per-run token budget. max_budget_usd is not enforced by AForge — bound a run with max_turns (becomes --turns) plus this.
AGENTFIELD_HARNESS_TIMEOUT_SECONDSenv1800Wall-clock cap the SDK passes to AForge as --timeout.
AGENTFIELD_HARNESS_PROVIDERenvaforgeSwitches the default provider process-wide. Accepts aforge, claude-code, codex, gemini, opencode (plus grok, Python SDK only).
providerHarnessConfig / per-call optionaforgeExplicit provider for this agent or this call. Highest precedence.
modelHarnessConfig / per-call optionemptyEmpty means "use the provider's own default". Set it to pin a specific model.

Provider precedence

Provider selection resolves in exactly this order — first match wins:

  1. An explicit provider on the call options, then on the agent's HarnessConfig.
  2. The AGENTFIELD_HARNESS_PROVIDER environment variable.
  3. aforge.

model follows the same shape: an explicit value wins, and an empty value means the provider picks its own default. model no longer defaults to "sonnet" — that was a Claude-specific value, and it now lives inside the claude-code provider, so explicit claude-code users see no change.

Operating it

# (Re)install or repair the AForge binary in ~/.agentfield/bin.
af aforge ensure

# Check the binary and its version, and report which auth env vars are set.
af harness doctor --provider aforge

af harness doctor works for every provider — swap --provider for claude-code, codex, gemini, opencode, or grok to check those instead.

If the binary is missing — a pip install agentfield-only setup, an install with --no-aforge, or AGENTFIELD_SKIP_AFORGE=1 — the first default-provider harness call fails with a provider-unavailable error that names the fix. Run af aforge ensure (or set AFORGE_BIN to an existing binary) and retry.

Choose a different worker

The default is a starting point, not a lock-in. AgentField's harness is a uniform loop with a swappable worker, so orchestrating Claude Code — or a fleet of them — is one field.

# Same call, different worker.
plan = await app.harness(prompt, provider="claude-code", permission_mode="plan", schema=ChangePlan)
edits = await app.harness(apply, provider="codex", permission_mode="auto", schema=EditReport)

Each override has its own install and credential (Claude Code differs per SDK):

providerInstallCredentialDocs
aforge (default)ships with afOPENROUTER_API_KEYthis page
claude-codePython: pip install 'agentfield[harness-claude]' · TypeScript: npm install @anthropic-ai/claude-agent-sdk · Go: npm install -g @anthropic-ai/claude-codeANTHROPIC_API_KEYAnthropic — Claude Code
codexnpm install -g @openai/codexOPENAI_API_KEY (or codex login)OpenAI — Codex
gemininpm install -g @google/gemini-cliGEMINI_API_KEY or GOOGLE_API_KEY (or sign in by running gemini once)Google — Gemini CLI
opencodecurl -fsSL https://opencode.ai/install | bashwhatever opencode auth login configuresOpenCode

Nothing else in the loop changes: same schema binding, same turn caps, same retry ladder, same HarnessResult. That is what makes retry-with-fallback, tournaments, and adversarial verifier patterns cheap — see Harness.

See also