Harness Methods

Dispatch multi-turn coding tasks to external coding agents

Harness Methods

Dispatch multi-turn coding tasks to external coding agents

Go SDK harness support is planned for a future release (Phase 3 of the harness implementation). The API below reflects the design specification. For production use today, see the Python SDK or TypeScript SDK.

This feature is under active development as part of Epic #208. The interface described here is the current design target and may change before the final release. Watch the repository for updates.

The harness methods will let a Go agent dispatch multi-turn coding tasks to an external coding agent (Claude Code, Codex, Gemini CLI, or Amp) and collect the result. The Go SDK will use a CLI subprocess strategy for all four providers, meaning the harness spawns the provider's CLI binary, streams its output, and returns a structured result once the task completes.

This approach keeps the Go SDK free of provider-specific API dependencies while still supporting the full range of harness providers available in the Python and TypeScript SDKs.


Planned interface

HarnessConfig

HarnessConfig controls how the harness runs the coding agent. Pass a pointer to HarnessConfig as the third argument to agent.Harness.

type HarnessConfig struct {
    Provider       string
    Model          string
    MaxTurns       int
    MaxBudgetUSD   *float64
    MaxRetries     int
    Tools          []string
    PermissionMode string
    SystemPrompt   string
    Env            map[string]string
    Cwd            string
}

Prop

Type


HarnessResult

Harness returns a HarnessResult on both success and failure. Check IsError before using Result.

type HarnessResult struct {
    Result       *string
    Parsed       interface{}
    IsError      bool
    ErrorMessage *string
    CostUSD      *float64
    NumTurns     int
    DurationMs   int
    SessionID    string
    Messages     []map[string]interface{}
}

Prop

Type


Planned usage

The Harness method will be available on the *agentfield.Client type, matching the pattern used by other client methods.

package main

import (
    "context"
    "fmt"
    "log"

    agentfield "github.com/Agent-Field/agentfield/sdk/go/client"
)

func main() {
    ctx := context.Background()

    c, err := agentfield.NewClient(agentfield.ClientOptions{
        ServerURL: "https://api.agentfield.com",
        APIKey:    "af-...",
    })
    if err != nil {
        log.Fatal(err)
    }

    budget := 2.50

    result, err := c.Harness(ctx, "Add input validation to the CreateUser handler and write unit tests for it.", &agentfield.HarnessConfig{
        Provider:       "claude-code",
        MaxTurns:       20,
        MaxBudgetUSD:   &budget,
        PermissionMode: "acceptEdits",
        Cwd:            "/workspace/myservice",
    })
    if err != nil {
        log.Fatalf("harness call failed: %v", err)
    }

    if result.IsError {
        log.Fatalf("coding agent returned an error: %s", *result.ErrorMessage)
    }

    fmt.Printf("Task completed in %d turns (%.0f ms)\n", result.NumTurns, float64(result.DurationMs))
    fmt.Printf("Cost: $%.4f\n", *result.CostUSD)
    fmt.Println(*result.Result)
}

The Harness method signature above is the planned design. The actual method will not exist until Phase 3 ships. Calling it today will result in a compile error.


Complete examples

Dispatch a straightforward refactoring task and print the result:

package main

import (
    "context"
    "fmt"
    "log"

    agentfield "github.com/Agent-Field/agentfield/sdk/go/client"
)

func runRefactor(ctx context.Context, c *agentfield.Client) error {
    result, err := c.Harness(ctx,
        "Refactor the database package to use connection pooling. Keep the public API unchanged.",
        &agentfield.HarnessConfig{
            Provider: "claude-code",
            MaxTurns: 30,
            Cwd:      "/workspace/myapp",
        },
    )
    if err != nil {
        return fmt.Errorf("harness error: %w", err)
    }

    if result.IsError {
        return fmt.Errorf("agent failed: %s", *result.ErrorMessage)
    }

    fmt.Printf("Done in %d turns\n", result.NumTurns)
    fmt.Println(*result.Result)
    return nil
}

Retry up to three times on transient failures:

package main

import (
    "context"
    "fmt"
    "log"

    agentfield "github.com/Agent-Field/agentfield/sdk/go/client"
)

func runWithRetries(ctx context.Context, c *agentfield.Client) error {
    result, err := c.Harness(ctx,
        "Fix all failing tests in the auth package.",
        &agentfield.HarnessConfig{
            Provider:   "claude-code",
            MaxTurns:   25,
            MaxRetries: 3,
            Cwd:        "/workspace/myapp",
        },
    )
    if err != nil {
        return fmt.Errorf("harness error after retries: %w", err)
    }

    if result.IsError {
        return fmt.Errorf("agent failed after retries: %s", *result.ErrorMessage)
    }

    fmt.Printf("Session: %s\n", result.SessionID)
    fmt.Println(*result.Result)
    return nil
}

Cap spending and handle the case where the agent hits the limit:

package main

import (
    "context"
    "fmt"

    agentfield "github.com/Agent-Field/agentfield/sdk/go/client"
)

func runWithBudget(ctx context.Context, c *agentfield.Client) error {
    cap := 1.00

    result, err := c.Harness(ctx,
        "Migrate all SQL queries in the reports package to use parameterized statements.",
        &agentfield.HarnessConfig{
            Provider:     "claude-code",
            MaxTurns:     40,
            MaxBudgetUSD: &cap,
            Cwd:          "/workspace/myapp",
        },
    )
    if err != nil {
        return fmt.Errorf("harness error: %w", err)
    }

    if result.IsError {
        return fmt.Errorf("agent stopped: %s", *result.ErrorMessage)
    }

    if result.CostUSD != nil {
        fmt.Printf("Spent $%.4f of $%.2f budget\n", *result.CostUSD, cap)
    }

    fmt.Println(*result.Result)
    return nil
}

Supported providers

All four providers will be supported via CLI subprocess. The Go SDK does not use provider HTTP APIs directly.

ProviderCLI binaryNotes
claude-codeclaudeDefault provider. Requires Claude Code CLI installed and authenticated.
codexcodexRequires OpenAI Codex CLI installed and an OPENAI_API_KEY in the environment.
geminigeminiRequires Gemini CLI installed and a GEMINI_API_KEY in the environment.
ampampRequires Amp CLI installed and authenticated.

The CLI binary must be on PATH or its full path must be provided via the Env field (e.g., "PATH": "/usr/local/bin:...") in HarnessConfig.