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 Development — ctx.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
| Provider | provider value | Binary | Credentials |
|---|---|---|---|
| Claude Code | "claude-code" | claude | ANTHROPIC_API_KEY |
| OpenAI Codex | "codex" | codex | OPENAI_API_KEY |
| Gemini | "gemini" | gemini | GEMINI_API_KEY |
| OpenCode | "opencode" | opencode | Provider-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
Related
- ctx.ai() - LLM calls without a subprocess
- agent.call() - Invoke another agent from a reasoner
- HarnessConfig - Full agent-level harness configuration
- Harness Providers - Provider setup, credentials, and binary installation