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
| Model | Use Case |
|---|---|
claude-sonnet-4-20250514 | Balanced performance (default) |
claude-opus-4-20250514 | Complex reasoning |
claude-haiku-3-20250307 | Fast, 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)}`);