Signal-based observability infrastructure for production AI agents.
Open Harness provides a reactive, signal-based architecture for building observable AI agent systems:
- Signal-Based Architecture: All agent events flow as typed signals through a central bus
- Full Observability: Subscribe to any signal pattern for logging, metrics, or custom handlers
- Harness Adapters: Unified interface for Claude, OpenAI, and other AI providers
- Replay Testing: Record and replay agent interactions for deterministic tests
bun add @open-harness/coreimport { ClaudeHarness } from "@open-harness/core";
import { SignalBus, attachReporter, createConsoleReporter } from "@open-harness/core";
// Create a signal bus for observability
const bus = new SignalBus();
// Attach a console reporter to see all signals
attachReporter(bus, createConsoleReporter());
// Create a harness for Claude
const harness = new ClaudeHarness({ model: "claude-sonnet-4-20250514" });
// Run the harness - it yields signals as the agent streams
const input = {
messages: [{ role: "user", content: "What is quantum computing?" }],
};
for await (const signal of harness.run(input, { signal: new AbortController().signal })) {
bus.emit(signal); // Route signals through the bus
// Handle specific signals
if (signal.name === "harness:text:delta") {
process.stdout.write(signal.payload.content);
}
}- Full Documentation - Tutorials, guides, and API reference
- Quickstart Tutorial - Run your first harness
- Architecture - Understand the signal-based design
- Contributing Guide - How to contribute to Open Harness
Everything in Open Harness is a signal. Signals are typed events with a name, payload, and metadata:
interface Signal<T = unknown> {
name: string; // e.g., "harness:text:delta"
payload: T; // Typed payload
timestamp: number; // When the signal was created
source?: SignalSource; // Where it came from
}The central dispatcher for all signals. Subscribe to patterns and handle events:
const bus = new SignalBus();
// Subscribe to all harness signals
bus.subscribe("harness:*", (signal) => {
console.log(signal.name, signal.payload);
});
// Subscribe to specific patterns
bus.subscribe("harness:text:*", (signal) => {
// Handle text deltas and completions
});Harnesses wrap AI providers and emit signals as async generators:
const harness = new ClaudeHarness();
for await (const signal of harness.run(input, ctx)) {
// Signals: harness:start, harness:text:delta, harness:tool:call, harness:end, etc.
}- Reactive Signal Architecture: Typed signals flow through a central bus
- Pattern Matching: Subscribe to signals using glob patterns (
harness:*,harness:text:*) - Multiple Reporters: Console, metrics, custom reporters attach to the bus
- Harness Adapters: Claude, OpenAI Codex, with more coming
- Snapshot State: Derive point-in-time state from signal history
- Replay Testing: Record signals and replay for deterministic tests
# Clone the repository
git clone https://github.com/open-harness/open-harness.git
cd open-harness
# Install dependencies
bun install
# Run tests
bun run test
# Type checking
bun run typecheck
# Lint and format
bun run lintopen-harness/
├── apps/
│ └── docs/ # Documentation site (Next.js + Fumadocs)
├── packages/
│ ├── adapters/
│ │ └── harnesses/ # Harness implementations (Claude, OpenAI)
│ ├── internal/
│ │ ├── signals/ # SignalBus, stores, reporters
│ │ └── signals-core/ # Signal primitives, Harness interface
│ └── open-harness/
│ ├── core/ # Public core API
│ ├── testing/ # Test utilities
│ └── vitest/ # Vitest matchers
├── specs/ # Feature specifications
└── .beads/ # Issue tracking
When using harnesses with Claude, authentication is handled automatically through the Claude Code subscription:
# Live tests work automatically with subscription auth
bun run test:liveDo not set ANTHROPIC_API_KEY - the SDK handles auth through your Claude Code subscription.
We use a standard branching model:
master (release ~1x/month)
↑
dev (integration branch)
↑
feature/* (your work)
- Create feature branches from
dev - PRs target
devfor integration devmerges tomasterfor releases
We use Beads for lightweight, git-native issue tracking.
# Install beads CLI (if not already installed)
curl -sSL https://raw.githubusercontent.com/steveyegge/beads/main/scripts/install.sh | bash
# The repo is already configured - just start using it
bd list # See open issues
bd show <id> # View issue details
bd create # Create new issueSync Branch Pattern: Issue data lives on a dedicated beads-sync branch, not in your feature branches. This prevents merge conflicts and keeps code branches clean.
Your code: Beads data:
master ← dev ← feat/* beads-sync (auto-synced)
Daily workflow:
# Start of session - context auto-injected via hooks
# Work on issues
bd start <id> # Mark issue in-progress
bd comment <id> # Add progress notes
# End of session - ALWAYS sync before stopping
bd sync # Commits + pushes beads changes| Command | Description |
|---|---|
bd list |
List open issues |
bd create |
Create new issue |
bd show <id> |
View issue details |
bd start <id> |
Start working on issue |
bd close <id> |
Close completed issue |
bd sync |
Sync changes to remote |
bd status |
Show database status |
bd doctor |
Health check |
- No merge conflicts - Feature branches don't carry beads data
- Multi-agent friendly - Agents can work in parallel without collision
- Clean PRs - Code reviews aren't cluttered with issue metadata
If you clone this repo fresh:
# The sync-branch is already configured in .beads/config.yaml
# Just make sure your local daemon knows about it:
bd config set sync.branch beads-sync
bd hooks install- Create a feature branch from
dev - Make your changes
- Run
bun run typecheck && bun run lint - Create PR targeting
dev - Ensure
bd syncis run before ending your session
See CONTRIBUTING.md for full guidelines.
This is an alpha release. Expect:
- evolving APIs and potentially breaking changes
- documentation gaps that we're actively improving
- missing features that are planned for future releases
We welcome feedback, bug reports, and contributions as we prepare for beta.
MIT - see LICENSE for details