Open Harness
Concepts

Signal System

Signal-based communication in Open Harness

Signal System

Everything in Open Harness communicates through signals.

Core Principle

Signals are the single source of truth for system state.

Instead of direct method calls, agents emit and subscribe to signals. This provides:

  • Observability: See exactly what happened
  • Replay: Reconstruct state from signals
  • Decoupling: Agents don't know about each other
  • Testing: Deterministic signal replay in CI

Signal Structure

All signals share a common structure:

interface Signal {
  id: string;           // Unique signal ID ("sig_abc123")
  name: string;         // Signal name ("analysis:complete")
  payload: unknown;     // Signal-specific data
  timestamp: string;    // ISO timestamp
  source?: {
    agent?: string;     // Which agent emitted this
    parent?: string;    // Parent signal (causality chain)
  };
}

Signal Categories

Workflow Lifecycle

{ name: "workflow:start", payload: { state: {...} } }
{ name: "workflow:end", payload: { state: {...}, metrics: {...} } }

Agent Signals

{ name: "agent:activated", payload: { agent: "analyzer" } }
{ name: "agent:skipped", payload: { agent: "reviewer", reason: "when guard false" } }
{ name: "agent:completed", payload: { agent: "analyzer", output: "..." } }

Harness Signals

{ name: "harness:start", payload: { model: "claude-sonnet-4-20250514" } }
{ name: "harness:end", payload: { usage: { inputTokens: 100, outputTokens: 50 } } }
{ name: "harness:error", payload: { error: "Rate limited" } }

Text Streaming

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

Thinking (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: "file contents..." } }

State Changes

{ name: "state:analysis:changed", payload: { value: "new analysis" } }
{ name: "state:score:changed", payload: { value: 0.95 } }

Custom Signals

Agents can emit custom signals via emits:

const analyzer = agent({
  prompt: "Analyze the input",
  activateOn: ["workflow:start"],
  emits: ["analysis:complete"],  // Custom signal
});

Subscribing to Signals

Agents subscribe to signals with activateOn:

// Activate on workflow lifecycle
activateOn: ["workflow:start"]

// Activate on custom signals
activateOn: ["analysis:complete", "review:done"]

// Activate on harness events
activateOn: ["harness:end"]

// Activate on state changes
activateOn: ["state:analysis:changed"]

Pattern Matching

Signal patterns support wildcards:

// Single segment wildcard
"agent:*"  // Matches agent:activated, agent:completed, etc.

// Multi-segment wildcard
"harness:**"  // Matches harness:start, harness:end, harness:error

// Prefix matching
"*:complete"  // Matches analysis:complete, review:complete, etc.

Signal Flow Example

Causality Tracking

Signals include source tracking for debugging:

const signals = result.signals;

// Find what triggered the reviewer
const reviewerActivation = signals.find(
  s => s.name === "agent:activated" && s.payload.agent === "reviewer"
);
console.log("Triggered by:", reviewerActivation.source.parent);

Signal Trace

Every runReactive returns the full signal trace:

const result = await runReactive({ /* ... */ });

console.log(result.signals);
// [
//   { name: "workflow:start", ... },
//   { name: "agent:activated", payload: { agent: "analyzer" } },
//   { name: "harness:start", ... },
//   { name: "text:delta", ... },
//   { name: "harness:end", ... },
//   { name: "analysis:complete", ... },
//   { name: "workflow:end", ... },
// ]

Next Steps

On this page