The composition pattern
Harnesses compose because they all implement the same interface:invoke() returns an async iterable of events. This means you can wrap one harness around another to add behavior without modifying the inner harness.
The agent harness is the canonical example:
- Calls
invoke()on the inner provider harness - Collects events from the provider
- Adds new behavior (permission checks, tool execution, iteration)
- Yields events to its own consumers
How the agent wraps a provider
Let’s trace the composition insideharness/agent.ts:31:
Agent harness wrapping Zen provider
- The agent receives the provider as
options.harness - It calls
harness.invoke()inside its own generator - It re-yields some events unchanged (like
text) - It adds new events (like
harness_start,relay,tool_result) - It loops, calling the provider multiple times with updated messages
Layering behavior
Because harnesses share an interface, you can stack them:Three-layer composition
- Receives events from the layer below
- Optionally transforms, filters, or adds events
- Yields to the layer above
Common composition patterns
Logging wrapper
Log every event passing through:Retry wrapper
Retry on errors:Rate limiting wrapper
Throttle invocations:Caching wrapper
Cache responses by message hash:Event transformation
Wrappers can transform events as they pass through:Prefix all text with metadata
Selective passthrough
Wrappers can filter events:Hide reasoning from consumers
Multi-provider fallback
Compose multiple providers with fallback logic:Composition vs modification
Composition has key advantages over modifying harness internals:No coupling
No coupling
Wrappers don’t need to understand the inner harness implementation. They only need to handle events.
Reusable
Reusable
A logging wrapper works with any harness: providers, agents, or custom harnesses.
Testable
Testable
You can test wrappers in isolation using a deterministic harness that yields canned events.
Mixable
Mixable
Stack wrappers in any order: logging + retry + rate limiting + caching.
Testing with deterministic harness
The deterministic harness lets you test composition without calling real LLMs:Real-world composition
Here’s how the orchestrator composes harnesses for production:From orchestrator.ts:94
createGeneratorHarness()creates a Zen providercreateAgentHarness()wraps it to add tool execution and permissionsorchestrator.spawn()wraps that in multiplexing and relay management- Each layer adds behavior while preserving the event stream interface
Next steps
Custom Harnesses
Build your own harness wrappers
Custom Provider
Implement a provider harness for a new LLM
Agent API
Full agent harness implementation reference
Orchestrator
Multi-agent composition patterns
