agent.harness()

Dispatch multi-turn coding tasks to external coding agents

The ctx.harness() method dispatches multi-turn coding tasks to an external coding agent (Claude Code, Codex, Gemini, or OpenCode). It runs the agent in a subprocess, captures its output, and returns a typed result with cost tracking, turn counts, and optional structured parsing via Zod.

Under Active Developmentctx.harness() is tracked under Epic #208. The API surface is stable for the providers listed below, but some options (particularly permissionMode and tools) may change before the 1.0 release.

Each provider requires a binary on PATH and the appropriate credentials in the environment. See Harness Providers for setup instructions.

Basic Usage

import { Agent } from '@agentfield/sdk';

const agent = new Agent({ nodeId: 'code-agent' });

agent.reasoner('refactor', async (ctx) => {
  const { filePath, instructions } = ctx.input;

  const result = await ctx.harness(
    `Refactor the file at ${filePath}. ${instructions}`,
    { provider: 'claude-code' }
  );

  return {
    output: result.text,
    turns: result.numTurns,
    costUsd: result.costUsd,
  };
});

Structured Output with Zod

When you pass a schema, the agent's final text output is parsed and validated. The parsed field on the result is typed to your schema.

import { z } from 'zod';
import { Agent } from '@agentfield/sdk';

const RefactorResultSchema = z.object({
  filesChanged: z.array(z.string()),
  summary: z.string(),
  breakingChanges: z.boolean(),
});

const agent = new Agent({ nodeId: 'code-agent' });

agent.reasoner('structured_refactor', async (ctx) => {
  const result = await ctx.harness(
    `Refactor the auth module. Return JSON matching: { filesChanged, summary, breakingChanges }`,
    {
      provider: 'claude-code',
      schema: RefactorResultSchema,
      maxTurns: 20,
    }
  );

  if (result.isError) {
    throw new Error(result.errorMessage ?? 'Harness failed');
  }

  // result.parsed is typed as { filesChanged: string[], summary: string, breakingChanges: boolean }
  return result.parsed;
});

When schema is provided, the SDK attempts to parse the agent's final message as JSON. If parsing fails, parsed is null and isError remains false — the raw text is still available via result.text.

With Custom Environment and Working Directory

agent.reasoner('run_in_repo', async (ctx) => {
  const result = await ctx.harness(
    'Add unit tests for all exported functions in src/utils.ts',
    {
      provider: 'codex',
      cwd: '/workspace/my-repo',
      env: {
        NODE_ENV: 'test',
        DATABASE_URL: ctx.input.dbUrl,
      },
      maxBudgetUsd: 2.0,
    }
  );

  return { output: result.text, turns: result.numTurns };
});

Supported Providers

Providerprovider valueBinaryCredentials
Claude Code"claude-code"claudeANTHROPIC_API_KEY
OpenAI Codex"codex"codexOPENAI_API_KEY
Gemini"gemini"geminiGEMINI_API_KEY
OpenCode"opencode"opencodeProvider-specific

Parameters

Prop

Type

HarnessConfig

HarnessConfig is the agent-level configuration object passed when constructing an Agent or registering a reasoner. Per-call options in ctx.harness() override these defaults.

Prop

Type

HarnessResult

ctx.harness() returns a HarnessResult<T> where T is inferred from the schema option (or unknown when no schema is provided).

Prop

Type