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.
| Provider | CLI binary | Notes |
|---|---|---|
claude-code | claude | Default provider. Requires Claude Code CLI installed and authenticated. |
codex | codex | Requires OpenAI Codex CLI installed and an OPENAI_API_KEY in the environment. |
gemini | gemini | Requires Gemini CLI installed and a GEMINI_API_KEY in the environment. |
amp | amp | Requires 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.
Related
- Go AI Client - Direct LLM calls without a coding agent subprocess
- Harness Providers Guide - Choosing and configuring providers across all SDKs
- Approval Methods - Pause agent execution for human review