Open Harness
Guides

Claude Harness

Configure the ClaudeHarness for Anthropic models

Claude Harness

The ClaudeHarness connects Open Harness to Anthropic's Claude models.

Basic Usage

import { createWorkflow, ClaudeHarness } from "@open-harness/core";

type State = { input: string; result: string | null };
const { agent, runReactive } = createWorkflow<State>();

const analyzer = agent({
  prompt: "Analyze: {{ state.input }}",
  activateOn: ["workflow:start"],
  emits: ["analysis:complete"],
  updates: "result",
});

const result = await runReactive({
  agents: { analyzer },
  state: { input: "Hello world", result: null },
  harness: new ClaudeHarness(),
  endWhen: (s) => s.result !== null,
});

Configuration Options

const harness = new ClaudeHarness({
  model: "claude-sonnet-4-20250514",  // Model to use
  maxTokens: 4096,                     // Max output tokens
  temperature: 0.7,                    // Sampling temperature
});

Available Models

ModelUse Case
claude-sonnet-4-20250514Balanced performance (default)
claude-opus-4-20250514Complex reasoning
claude-haiku-3-20250307Fast, simple tasks

Per-Agent Harness

Override the harness for specific agents:

const complexAgent = agent({
  prompt: "Solve this complex problem: {{ state.problem }}",
  activateOn: ["workflow:start"],
  harness: new ClaudeHarness({ model: "claude-opus-4-20250514" }),
});

const quickAgent = agent({
  prompt: "Quick classification: {{ state.input }}",
  activateOn: ["workflow:start"],
  harness: new ClaudeHarness({ model: "claude-haiku-3-20250307" }),
});

Signals Emitted

The ClaudeHarness emits standard harness signals:

Lifecycle

{ name: "harness:start", payload: { model: "claude-sonnet-4-20250514" } }
{ name: "harness:end", payload: { usage: { inputTokens, outputTokens }, cost } }
{ name: "harness:error", payload: { error: "..." } }

Text Streaming

{ name: "text:delta", payload: { content: "The " } }
{ name: "text:complete", payload: { content: "The analysis shows..." } }

Extended Thinking

When using models with extended thinking:

{ name: "thinking:delta", payload: { content: "Let me consider..." } }
{ name: "thinking:complete", payload: { content: "..." } }

Tool Usage

{ name: "tool:call", payload: { name: "Read", input: { path: "/file.ts" } } }
{ name: "tool:result", payload: { name: "Read", output: "..." } }

Authentication

The harness uses Claude Code subscription authentication. No API key is required - authentication is handled automatically when running from within Claude Code.

Claude Code Required

Open Harness is designed to work with Claude Code subscription authentication. Run your agents from within Claude Code for automatic authentication.

Session Support

The ClaudeHarness supports session persistence for multi-turn conversations:

// First run - creates a session
const result1 = await runReactive({
  agents: { assistant },
  state: { messages: ["Hello"], response: null },
  harness,
});

// Session ID is captured in harness:end signal
const sessionId = result1.signals
  .find(s => s.name === "harness:end")
  ?.payload.sessionId;

// Resume the session later
// (Implementation depends on your session storage strategy)

Error Handling

The harness emits harness:error signals on failure:

const result = await runReactive({
  agents: { analyzer },
  state: initialState,
  harness,
});

const errors = result.signals.filter(s => s.name === "harness:error");
if (errors.length > 0) {
  console.error("Harness errors:", errors);
}

Usage Metrics

Extract usage from harness:end signals:

const harnessEnds = result.signals.filter(s => s.name === "harness:end");

const totalTokens = harnessEnds.reduce((sum, s) => {
  return sum + (s.payload.usage?.inputTokens || 0) + (s.payload.usage?.outputTokens || 0);
}, 0);

const totalCost = harnessEnds.reduce((sum, s) => {
  return sum + (s.payload.cost || 0);
}, 0);

console.log(`Tokens: ${totalTokens}, Cost: $${totalCost.toFixed(4)}`);

Next Steps

On this page