app.harness()

Dispatch multi-turn coding tasks to external coding agents

app.harness()

Dispatch multi-turn coding tasks to external coding agents

Runs a prompt through a multi-turn coding agent (Claude Code, Codex, Gemini CLI, or OpenCode) and returns a structured result. Where app.ai() handles single-turn LLM calls, app.harness() handles tasks that require browsing files, editing code, running tests, and iterating across many turns.

Bring Your Own Credentials: Each provider requires its own API key or binary installed in your environment. See the Harness Providers guide for setup instructions per provider.

Under Active Development: app.harness() is being built as part of Epic #208. The API shape is stable but some providers and features are still being rolled out. Check the epic for current status.

Basic Example

from agentfield import Agent, HarnessConfig

app = Agent(
    node_id="coder",
    harness_config=HarnessConfig(
        provider="claude-code",
        model="sonnet",
    ),
)

result = await app.harness("Fix the auth bug in src/auth.py")
print(result.text)

Parameters

Prop

Type

Returns: HarnessResult

HarnessConfig

Set agent-level defaults at construction time. All fields can be overridden per-call.

from agentfield import Agent, HarnessConfig

app = Agent(
    node_id="coder",
    harness_config=HarnessConfig(
        provider="claude-code",   # Required — no implicit default
        model="sonnet",
        max_turns=50,
        max_budget_usd=2.0,
    ),
)

Prop

Type

HarnessResult

Prop

Type

Supported Providers

ProviderIntegrationRequired Credential
claude-codeclaude_agent_sdk (in-process Python SDK)ANTHROPIC_API_KEY
codexCLI subprocess codex exec --jsonCODEX_API_KEY
geminiCLI subprocess gemini --output-format jsonGEMINI_API_KEY
opencodeCLI subprocess opencode runProvider-dependent

Claude Code runs in-process via the claude_agent_sdk package. The other three providers run as CLI subprocesses and must be installed separately. See Harness Providers for installation and credential setup.

Examples

from agentfield import Agent, HarnessConfig

app = Agent(
    node_id="coder",
    harness_config=HarnessConfig(
        provider="claude-code",
        model="sonnet",
    ),
)

result = await app.harness(
    "Add input validation to the login endpoint in src/routes/auth.py",
    cwd="/my/project",
)

print(result.text)
print(f"Completed in {result.num_turns} turns, cost ${result.cost_usd:.4f}")
from pydantic import BaseModel
from agentfield import Agent, HarnessConfig

class BugFix(BaseModel):
    files_changed: list[str]
    summary: str
    tests_added: bool
    breaking_change: bool

app = Agent(
    node_id="coder",
    harness_config=HarnessConfig(
        provider="claude-code",
        model="sonnet",
    ),
)

fix = await app.harness(
    "Fix the race condition in src/worker.py and add regression tests",
    schema=BugFix,
    cwd="/my/project",
    max_turns=80,
)

# fix.parsed is a validated BugFix instance
print(fix.parsed.files_changed)   # ["src/worker.py", "tests/test_worker.py"]
print(fix.parsed.tests_added)     # True
print(fix.parsed.breaking_change) # False
from agentfield import Agent, HarnessConfig

# Agent defaults to claude-code
app = Agent(
    node_id="coder",
    harness_config=HarnessConfig(
        provider="claude-code",
        model="sonnet",
    ),
)

# Override to codex for this specific call
result = await app.harness(
    "Refactor the database layer to use async/await throughout",
    provider="codex",
    model="o3",
    max_turns=100,
    tools=["Read", "Write", "Edit", "Bash"],
    max_budget_usd=5.0,
    cwd="/my/project",
)

print(result.text)
from pydantic import BaseModel
from agentfield import Agent, HarnessConfig

class IssueFixResult(BaseModel):
    files_changed: list[str]
    summary: str
    tests_added: bool

app = Agent(
    node_id="issue-resolver",
    harness_config=HarnessConfig(
        provider="claude-code",
        model="sonnet",
        max_turns=150,
    ),
)

@app.reasoner
async def fix_issue(issue: dict) -> dict:
    """Resolve a GitHub issue by dispatching a coding agent."""
    result = await app.harness(
        f"Fix: {issue['title']}\n\n{issue['description']}",
        schema=IssueFixResult,
        cwd=issue["repo_path"],
        tools=["Read", "Write", "Edit", "Bash", "Glob", "Grep"],
    )

    if result.is_error:
        return {"error": result.error_message, "resolved": False}

    return {**result.parsed.model_dump(), "resolved": True}
from agentfield import Agent, HarnessConfig

app = Agent(
    node_id="coder",
    harness_config=HarnessConfig(
        provider="claude-code",
        model="sonnet",
        max_retries=3,
        max_budget_usd=3.0,
    ),
)

result = await app.harness(
    "Migrate the test suite from unittest to pytest",
    cwd="/my/project",
)

if result.is_error:
    print(f"Run failed: {result.error_message}")
    print(f"Completed {result.num_turns} turns before failure")
else:
    print(result.text)
    print(f"Cost: ${result.cost_usd:.4f} over {result.duration_ms}ms")

Under the Hood