Signals
Signal types and structure reference
Signals
All communication in Open Harness flows through typed signals.
Signal Structure
interface Signal {
id: string; // Unique identifier ("sig_abc123")
name: string; // Signal name ("analysis:complete")
payload: unknown; // Signal-specific data
timestamp: string; // ISO timestamp
source?: {
agent?: string; // Emitting agent
parent?: string; // Parent signal ID (causality)
};
}Workflow Signals
workflow:start
Emitted when workflow begins:
{
name: "workflow:start",
payload: {
state: { /* initial state */ },
agents: ["analyzer", "reviewer"],
}
}workflow:end
Emitted when workflow completes:
{
name: "workflow:end",
payload: {
state: { /* final state */ },
metrics: {
durationMs: 1234,
activations: 3,
},
terminatedEarly: false,
}
}Agent Signals
agent:activated
Emitted when an agent activates:
{
name: "agent:activated",
payload: {
agent: "analyzer",
triggeredBy: "workflow:start",
}
}agent:skipped
Emitted when an agent's when guard returns false:
{
name: "agent:skipped",
payload: {
agent: "reviewer",
reason: "when guard returned false",
}
}agent:completed
Emitted when an agent finishes:
{
name: "agent:completed",
payload: {
agent: "analyzer",
output: "Analysis result...",
}
}Harness Signals
harness:start
Emitted when harness begins execution:
{
name: "harness:start",
payload: {
model: "claude-sonnet-4-20250514",
}
}harness:end
Emitted when harness completes:
{
name: "harness:end",
payload: {
usage: {
inputTokens: 150,
outputTokens: 89,
},
cost: 0.0012,
sessionId: "session_abc123",
}
}harness:error
Emitted on harness failure:
{
name: "harness:error",
payload: {
error: "Rate limit exceeded",
retryAfter: 30,
}
}Text Signals
text:delta
Streaming text chunk:
{
name: "text:delta",
payload: {
content: "The ",
}
}text:complete
Full text after streaming:
{
name: "text:complete",
payload: {
content: "The analysis shows three key findings...",
}
}Thinking Signals
thinking:delta
Streaming thinking chunk (extended thinking):
{
name: "thinking:delta",
payload: {
content: "Let me consider the implications...",
}
}thinking:complete
Full thinking content:
{
name: "thinking:complete",
payload: {
content: "...",
}
}Tool Signals
tool:call
Tool invocation:
{
name: "tool:call",
payload: {
id: "call_abc123",
name: "Read",
input: {
path: "/src/index.ts",
},
}
}tool:result
Tool result:
{
name: "tool:result",
payload: {
id: "call_abc123",
name: "Read",
output: "file contents...",
error: null,
}
}State Signals
state:{key}:changed
Emitted when state changes:
{
name: "state:analysis:changed",
payload: {
value: "New analysis result",
previousValue: null,
}
}Custom Signals
Agents emit custom signals via emits:
const analyzer = agent({
prompt: "Analyze the input",
activateOn: ["workflow:start"],
emits: ["analysis:complete"],
});Emitted as:
{
name: "analysis:complete",
payload: { /* agent output */ },
source: { agent: "analyzer" },
}Signal Patterns
Use patterns to match signals:
| Pattern | Matches |
|---|---|
"workflow:start" | Exact match |
"agent:*" | agent:activated, agent:completed, etc. |
"harness:**" | All harness signals |
"*:complete" | text:complete, analysis:complete, etc. |
Working with Signals
Filter by Name
const agentSignals = result.signals.filter(s => s.name.startsWith("agent:"));Extract Usage
const harnessEnds = result.signals.filter(s => s.name === "harness:end");
const totalTokens = harnessEnds.reduce((sum, s) => {
const usage = s.payload.usage || { inputTokens: 0, outputTokens: 0 };
return sum + usage.inputTokens + usage.outputTokens;
}, 0);Build Causality Chain
import { getCausalityChain } from "@open-harness/core";
const chain = getCausalityChain(result.signals, targetSignalId);
// Returns array of signals leading to target