Events
Every serialized run event envelope, whether streamed over SSE, returned by fabro logs, or written to a JSONL sink, uses this structure:
{
"id": "019234ab-cdef-7890-abcd-ef1234567890",
"ts": "2026-04-01T12:00:00.123Z",
"run_id": "01JQXYZ...",
"event": "stage.completed",
"session_id": "ses_abc",
"parent_session_id": "ses_parent",
"node_id": "code",
"node_label": "Write Code",
"properties": { ... }
}
Envelope fields
| Field |
Type |
Description |
id |
string |
UUID v7 (time-ordered), unique per event |
ts |
string |
RFC 3339 timestamp with millisecond precision |
run_id |
string |
ULID of the run |
event |
string |
Dot-notation event name |
session_id |
string? |
Agent session id (agent events only) |
parent_session_id |
string? |
Parent agent session id (agent events only) |
node_id |
string? |
Node id (stage, checkpoint, agent, parallel branch, and other node-scoped events) |
node_label |
string? |
Display label for the node (defaults to node_id when not set separately) |
properties |
object |
Event-specific fields |
Run events
run.started
Emitted when the workflow run begins.
{
"id": "...", "ts": "...", "run_id": "...",
"event": "run.started",
"properties": {
"name": "my-workflow",
"base_branch": "main",
"base_sha": "abc123...",
"run_branch": "fabro/run-01JQXYZ",
"worktree_dir": "/tmp/fabro-worktrees/...",
"goal": "Fix the login bug"
}
}
| Property |
Type |
Description |
name |
string |
Workflow name |
base_branch |
string? |
Base git branch |
base_sha |
string? |
Base commit SHA |
run_branch |
string? |
Git branch created for this run |
worktree_dir |
string? |
Worktree directory path |
goal |
string? |
Workflow goal text |
Note: run_id is in the envelope, not in properties.
run.completed
Emitted when the workflow run finishes successfully (or with partial success).
{
"id": "...", "ts": "...", "run_id": "...",
"event": "run.completed",
"properties": {
"duration_ms": 45000,
"artifact_count": 3,
"status": "success",
"total_cost": 0.15,
"final_git_commit_sha": "def456...",
"usage": {
"input_tokens": 15000,
"output_tokens": 5000,
"total_tokens": 20000,
"reasoning_tokens": 2000,
"cache_read_tokens": 8000,
"cache_write_tokens": 3000,
"speed": "standard"
}
}
}
| Property |
Type |
Description |
duration_ms |
number |
Total run duration in milliseconds |
artifact_count |
number |
Number of artifacts produced |
status |
string |
Final status ("success", "fail", "partial_success") |
total_cost |
number? |
Aggregate cost in USD |
final_git_commit_sha |
string? |
Final HEAD SHA |
usage |
object? |
Aggregate token usage |
usage.input_tokens |
number |
Total input tokens |
usage.output_tokens |
number |
Total output tokens |
usage.total_tokens |
number |
Total tokens (input + output) |
usage.reasoning_tokens |
number? |
Total reasoning/thinking tokens |
usage.cache_read_tokens |
number? |
Total cache read tokens |
usage.cache_write_tokens |
number? |
Total cache write tokens |
usage.speed |
string? |
Speed tier |
usage.raw |
object? |
Raw provider-specific usage data |
run.failed
Emitted when the workflow run fails.
{
"id": "...", "ts": "...", "run_id": "...",
"event": "run.failed",
"properties": {
"error": "Handler error: compilation failed",
"duration_ms": 12000,
"git_commit_sha": "abc123..."
}
}
| Property |
Type |
Description |
error |
string |
Error message (Display representation) |
duration_ms |
number |
Run duration before failure |
git_commit_sha |
string? |
HEAD SHA at time of failure |
run.notice
Informational, warning, or error notice emitted during the run.
{
"id": "...", "ts": "...", "run_id": "...",
"event": "run.notice",
"properties": {
"level": "warn",
"code": "missing_env_var",
"message": "GITHUB_TOKEN not set, PR creation will be skipped"
}
}
| Property |
Type |
Description |
level |
string |
"info", "warn", or "error" |
code |
string |
Machine-readable notice code |
message |
string |
Human-readable message |
Stage events
stage.started
Emitted when a workflow node begins execution.
{
"id": "...", "ts": "...", "run_id": "...",
"event": "stage.started",
"node_id": "code",
"node_label": "Write Code",
"properties": {
"index": 1,
"handler_type": "agent",
"attempt": 1,
"max_attempts": 3
}
}
| Property |
Type |
Description |
index |
number |
Stage execution order index |
handler_type |
string |
Handler type ("agent", "prompt", "command", "conditional", "human", "parallel", etc.) |
attempt |
number |
Current attempt number (1-based) |
max_attempts |
number |
Maximum attempts allowed |
stage.completed
Emitted when a workflow node finishes execution.
{
"id": "...", "ts": "...", "run_id": "...",
"event": "stage.completed",
"node_id": "code",
"node_label": "Write Code",
"properties": {
"index": 1,
"duration_ms": 8000,
"status": "success",
"preferred_label": "tests_pass",
"suggested_next_ids": ["review"],
"usage": {
"model": "claude-sonnet-4-20250514",
"input_tokens": 5000,
"output_tokens": 2000,
"cache_read_tokens": 3000,
"cache_write_tokens": 1000,
"reasoning_tokens": 500,
"speed": "standard",
"cost": 0.05
},
"error": "lint failed",
"failure_class": "deterministic",
"failure_signature": "clippy::unused_import",
"context_updates": {"response.code": "done"},
"jump_to_node": "review",
"context_values": {"response.code": "done"},
"node_visits": {"code": 1},
"loop_failure_signatures": {"code|deterministic|clippy::unused_import": 2},
"restart_failure_signatures": {"code|transient_infra|timeout": 1},
"response": "done",
"notes": "All tests passing",
"files_touched": ["src/main.rs", "src/lib.rs"],
"attempt": 1,
"max_attempts": 3
}
}
| Property |
Type |
Description |
index |
number |
Stage execution order index |
duration_ms |
number |
Stage duration in milliseconds |
status |
string |
"success", "fail", "skipped", "partial_success", "retry" |
preferred_label |
string? |
Edge label hint for routing |
suggested_next_ids |
string[] |
Suggested successor node ids |
usage |
object? |
Token usage for this stage |
usage.model |
string |
Model identifier |
usage.input_tokens |
number |
Input tokens |
usage.output_tokens |
number |
Output tokens |
usage.cache_read_tokens |
number? |
Cache read tokens |
usage.cache_write_tokens |
number? |
Cache write tokens |
usage.reasoning_tokens |
number? |
Reasoning/thinking tokens |
usage.speed |
string? |
Speed tier |
usage.cost |
number? |
Estimated cost in USD |
error |
string? |
Error message (flattened from failure detail) |
failure_class |
string? |
"transient_infra", "deterministic", "budget_exhausted", "compilation_loop", "canceled", "structural" |
failure_signature |
string? |
Dedup key for repeated failures |
context_updates |
object? |
Context delta written by this stage |
jump_to_node |
string? |
Non-edge jump target |
context_values |
object? |
Full context snapshot after the stage |
node_visits |
object? |
Node visit counts after the stage |
loop_failure_signatures |
object? |
Loop failure signature counts |
restart_failure_signatures |
object? |
Restart failure signature counts |
response |
string? |
Full LLM or agent response text when produced by the stage |
notes |
string? |
Free-text notes |
files_touched |
string[] |
File paths modified |
attempt |
number |
Attempt number (1-based) |
max_attempts |
number |
Maximum attempts allowed |
Note: failure is flattened — the failure.message becomes error, failure.failure_class becomes failure_class, failure.failure_signature becomes failure_signature.
stage.failed
Emitted when a stage fails (before retry decision).
{
"id": "...", "ts": "...", "run_id": "...",
"event": "stage.failed",
"node_id": "code",
"node_label": "Write Code",
"properties": {
"index": 1,
"error": "compilation failed",
"failure_class": "deterministic",
"failure_signature": "rustc::E0308",
"will_retry": true
}
}
| Property |
Type |
Description |
index |
number |
Stage execution order index |
error |
string |
Error message (flattened from failure detail) |
failure_class |
string |
Failure category |
failure_signature |
string? |
Dedup key for repeated failures |
will_retry |
boolean |
Whether the stage will be retried |
stage.retrying
Emitted when a stage is about to be retried.
{
"id": "...", "ts": "...", "run_id": "...",
"event": "stage.retrying",
"node_id": "code",
"node_label": "Write Code",
"properties": {
"index": 1,
"attempt": 2,
"max_attempts": 3,
"delay_ms": 1000
}
}
| Property |
Type |
Description |
index |
number |
Stage execution order index |
attempt |
number |
Next attempt number |
max_attempts |
number |
Maximum attempts allowed |
delay_ms |
number |
Delay before retry in milliseconds |
stage.prompt
Emitted when a prompt is rendered for an LLM stage.
{
"id": "...", "ts": "...", "run_id": "...",
"event": "stage.prompt",
"node_id": "code",
"node_label": "code",
"properties": {
"text": "You are a coding agent. Fix the bug in..."
}
}
| Property |
Type |
Description |
text |
string |
Rendered prompt text |
Parallel events
parallel.started
Emitted when a parallel node begins executing branches.
{
"id": "...", "ts": "...", "run_id": "...",
"event": "parallel.started",
"properties": {
"branch_count": 3,
"join_policy": "all"
}
}
| Property |
Type |
Description |
branch_count |
number |
Number of parallel branches |
join_policy |
string |
Join policy |
parallel.branch.started
Emitted when a parallel branch begins.
{
"id": "...", "ts": "...", "run_id": "...",
"event": "parallel.branch.started",
"node_id": "branch_a",
"node_label": "branch_a",
"properties": {
"index": 0
}
}
| Property |
Type |
Description |
index |
number |
Branch index |
parallel.branch.completed
Emitted when a parallel branch finishes.
{
"id": "...", "ts": "...", "run_id": "...",
"event": "parallel.branch.completed",
"node_id": "branch_a",
"node_label": "branch_a",
"properties": {
"index": 0,
"duration_ms": 5000,
"status": "success"
}
}
| Property |
Type |
Description |
index |
number |
Branch index |
duration_ms |
number |
Branch duration in milliseconds |
status |
string |
Branch outcome status |
parallel.completed
Emitted when all parallel branches have finished.
{
"id": "...", "ts": "...", "run_id": "...",
"event": "parallel.completed",
"properties": {
"duration_ms": 12000,
"success_count": 2,
"failure_count": 1
}
}
| Property |
Type |
Description |
duration_ms |
number |
Total parallel duration |
success_count |
number |
Branches that succeeded |
failure_count |
number |
Branches that failed |
Interview events
interview.started
Emitted when a human-in-the-loop question is posed.
{
"id": "...", "ts": "...", "run_id": "...",
"event": "interview.started",
"node_id": "review",
"node_label": "review",
"properties": {
"question": "Does this look correct?",
"question_type": "approval"
}
}
| Property |
Type |
Description |
question |
string |
Question text |
question_type |
string |
Type of question |
interview.completed
Emitted when a human answers.
{
"id": "...", "ts": "...", "run_id": "...",
"event": "interview.completed",
"properties": {
"question": "Does this look correct?",
"answer": "yes",
"duration_ms": 30000
}
}
| Property |
Type |
Description |
question |
string |
Question text |
answer |
string |
Human's answer |
duration_ms |
number |
Time waiting for answer |
interview.timeout
Emitted when a human question times out.
{
"id": "...", "ts": "...", "run_id": "...",
"event": "interview.timeout",
"node_id": "review",
"node_label": "review",
"properties": {
"question": "Does this look correct?",
"duration_ms": 300000
}
}
| Property |
Type |
Description |
question |
string |
Question text |
duration_ms |
number |
Time waited before timeout |
Checkpoint events
checkpoint.completed
Emitted after a checkpoint is saved.
{
"id": "...", "ts": "...", "run_id": "...",
"event": "checkpoint.completed",
"node_id": "code",
"node_label": "code",
"properties": {
"status": "success",
"git_commit_sha": "abc123...",
"diff": "diff --git a/src/lib.rs b/src/lib.rs\n..."
}
}
| Property |
Type |
Description |
status |
string |
Checkpoint status |
git_commit_sha |
string? |
Commit SHA at checkpoint time |
diff |
string? |
Git diff captured for the checkpointed node |
checkpoint.failed
Emitted when checkpoint saving fails.
{
"id": "...", "ts": "...", "run_id": "...",
"event": "checkpoint.failed",
"node_id": "code",
"node_label": "code",
"properties": {
"error": "git commit failed: ..."
}
}
| Property |
Type |
Description |
error |
string |
Error message |
Git events
git.commit
{
"id": "...", "ts": "...", "run_id": "...",
"event": "git.commit",
"node_id": "code",
"node_label": "code",
"properties": {
"sha": "abc123..."
}
}
| Property |
Type |
Description |
sha |
string |
Commit SHA |
Note: node_id is optional — may be absent for non-stage commits.
git.push
{
"id": "...", "ts": "...", "run_id": "...",
"event": "git.push",
"properties": {
"branch": "fabro/run-01JQXYZ",
"success": true
}
}
| Property |
Type |
Description |
branch |
string |
Branch name |
success |
boolean |
Whether push succeeded |
git.branch
{
"id": "...", "ts": "...", "run_id": "...",
"event": "git.branch",
"properties": {
"branch": "fabro/run-01JQXYZ",
"sha": "abc123..."
}
}
| Property |
Type |
Description |
branch |
string |
Branch name |
sha |
string |
Branch HEAD SHA |
git.worktree.added
{
"id": "...", "ts": "...", "run_id": "...",
"event": "git.worktree.added",
"properties": {
"path": "/tmp/fabro-worktrees/...",
"branch": "fabro/run-01JQXYZ"
}
}
| Property |
Type |
Description |
path |
string |
Worktree directory path |
branch |
string |
Branch name |
git.worktree.removed
{
"id": "...", "ts": "...", "run_id": "...",
"event": "git.worktree.removed",
"properties": {
"path": "/tmp/fabro-worktrees/..."
}
}
| Property |
Type |
Description |
path |
string |
Worktree directory path |
git.fetch
{
"id": "...", "ts": "...", "run_id": "...",
"event": "git.fetch",
"properties": {
"branch": "main",
"success": true
}
}
| Property |
Type |
Description |
branch |
string |
Branch name |
success |
boolean |
Whether fetch succeeded |
git.reset
{
"id": "...", "ts": "...", "run_id": "...",
"event": "git.reset",
"properties": {
"sha": "abc123..."
}
}
| Property |
Type |
Description |
sha |
string |
Target commit SHA |
Routing events
edge.selected
Emitted when the engine selects the next edge to traverse.
{
"id": "...", "ts": "...", "run_id": "...",
"event": "edge.selected",
"properties": {
"from_node": "code",
"to_node": "review",
"label": "tests_pass",
"condition": "outcome=success",
"reason": "condition",
"preferred_label": "tests_pass",
"suggested_next_ids": ["review"],
"stage_status": "success",
"is_jump": false
}
}
| Property |
Type |
Description |
from_node |
string |
Source node id |
to_node |
string |
Target node id |
label |
string? |
Edge label |
condition |
string? |
Edge condition expression |
reason |
string |
Selection reason ("condition", "preferred_label", "jump", etc.) |
preferred_label |
string? |
Stage's preferred label hint |
suggested_next_ids |
string[] |
Stage's suggested next node ids |
stage_status |
string |
Outcome status that influenced routing |
is_jump |
boolean |
Whether this bypassed normal edge selection |
loop.restart
Emitted when execution loops back to an earlier node.
{
"id": "...", "ts": "...", "run_id": "...",
"event": "loop.restart",
"properties": {
"from_node": "review",
"to_node": "code"
}
}
| Property |
Type |
Description |
from_node |
string |
Node that triggered the restart |
to_node |
string |
Node to restart from |
Agent events
All agent events have node_id (the workflow stage), node_label, session_id, and parent_session_id in the envelope. The properties contain the inner agent event fields.
agent.session.started
{
"id": "...", "ts": "...", "run_id": "...",
"event": "agent.session.started",
"node_id": "code", "node_label": "code",
"session_id": "ses_abc", "parent_session_id": null,
"properties": {}
}
No properties.
agent.session.ended
{
"id": "...", "ts": "...", "run_id": "...",
"event": "agent.session.ended",
"node_id": "code", "node_label": "code",
"session_id": "ses_abc",
"properties": {}
}
No properties.
agent.processing.end
{
"id": "...", "ts": "...", "run_id": "...",
"event": "agent.processing.end",
"node_id": "code", "node_label": "code",
"session_id": "ses_abc",
"properties": {}
}
No properties.
agent.input
{
"id": "...", "ts": "...", "run_id": "...",
"event": "agent.input",
"node_id": "code", "node_label": "code",
"session_id": "ses_abc",
"properties": {
"text": "Fix the login bug in auth.rs"
}
}
| Property |
Type |
Description |
text |
string |
User input text |
agent.output.start
Signals the beginning of assistant text output.
{
"id": "...", "ts": "...", "run_id": "...",
"event": "agent.output.start",
"node_id": "code", "node_label": "code",
"session_id": "ses_abc",
"properties": {}
}
No properties.
agent.output.replace
Replaces the current in-progress assistant output buffers.
{
"id": "...", "ts": "...", "run_id": "...",
"event": "agent.output.replace",
"node_id": "code", "node_label": "code",
"session_id": "ses_abc",
"properties": {
"text": "I'll fix the login bug by...",
"reasoning": "The user wants..."
}
}
| Property |
Type |
Description |
text |
string |
Replacement assistant text |
reasoning |
string? |
Replacement reasoning text |
agent.message
Emitted when the assistant produces a complete message.
{
"id": "...", "ts": "...", "run_id": "...",
"event": "agent.message",
"node_id": "code", "node_label": "code",
"session_id": "ses_abc",
"properties": {
"text": "I've fixed the bug in auth.rs by...",
"model": "claude-sonnet-4-20250514",
"usage": {
"input_tokens": 3000,
"output_tokens": 1500,
"total_tokens": 4500,
"reasoning_tokens": 200,
"cache_read_tokens": 1000,
"cache_write_tokens": 500
},
"tool_call_count": 2
}
}
| Property |
Type |
Description |
text |
string |
Assistant message text |
model |
string |
Model identifier |
usage |
object |
Token usage for this message |
usage.input_tokens |
number |
Input tokens |
usage.output_tokens |
number |
Output tokens |
usage.total_tokens |
number |
Total tokens |
usage.reasoning_tokens |
number? |
Reasoning tokens |
usage.cache_read_tokens |
number? |
Cache read tokens |
usage.cache_write_tokens |
number? |
Cache write tokens |
usage.speed |
string? |
Speed tier |
usage.raw |
object? |
Raw provider-specific usage |
tool_call_count |
number |
Number of tool calls in this turn |
agent.text.delta
Streaming text chunk from the assistant.
{
"id": "...", "ts": "...", "run_id": "...",
"event": "agent.text.delta",
"node_id": "code", "node_label": "code",
"session_id": "ses_abc",
"properties": {
"delta": "I'll start by reading"
}
}
| Property |
Type |
Description |
delta |
string |
Text chunk |
agent.reasoning.delta
Streaming reasoning/thinking chunk from the assistant.
{
"id": "...", "ts": "...", "run_id": "...",
"event": "agent.reasoning.delta",
"node_id": "code", "node_label": "code",
"session_id": "ses_abc",
"properties": {
"delta": "The user needs me to..."
}
}
| Property |
Type |
Description |
delta |
string |
Reasoning text chunk |
agent.tool.started
Emitted when the agent begins a tool call.
{
"id": "...", "ts": "...", "run_id": "...",
"event": "agent.tool.started",
"node_id": "code", "node_label": "code",
"session_id": "ses_abc",
"properties": {
"tool_name": "read_file",
"tool_call_id": "call_abc123",
"arguments": {"path": "src/auth.rs"}
}
}
| Property |
Type |
Description |
tool_name |
string |
Tool name |
tool_call_id |
string |
Unique tool call id |
arguments |
object |
Tool call arguments |
agent.tool.output.delta
Streaming tool output chunk.
{
"id": "...", "ts": "...", "run_id": "...",
"event": "agent.tool.output.delta",
"node_id": "code", "node_label": "code",
"session_id": "ses_abc",
"properties": {
"delta": "fn login(user: &str)..."
}
}
| Property |
Type |
Description |
delta |
string |
Output text chunk |
agent.tool.completed
Emitted when a tool call finishes.
{
"id": "...", "ts": "...", "run_id": "...",
"event": "agent.tool.completed",
"node_id": "code", "node_label": "code",
"session_id": "ses_abc",
"properties": {
"tool_name": "read_file",
"tool_call_id": "call_abc123",
"output": "fn login(user: &str) -> Result<Token>...",
"is_error": false
}
}
| Property |
Type |
Description |
tool_name |
string |
Tool name |
tool_call_id |
string |
Unique tool call id |
output |
any |
Tool output (string or structured) |
is_error |
boolean |
Whether the tool returned an error |
agent.error
Emitted when the agent encounters an error.
{
"id": "...", "ts": "...", "run_id": "...",
"event": "agent.error",
"node_id": "code", "node_label": "code",
"session_id": "ses_abc",
"properties": {
"error": { ... }
}
}
| Property |
Type |
Description |
error |
object |
AgentError (serialized) |
agent.warning
{
"id": "...", "ts": "...", "run_id": "...",
"event": "agent.warning",
"node_id": "code", "node_label": "code",
"session_id": "ses_abc",
"properties": {
"kind": "token_limit",
"message": "Approaching context window limit",
"details": {}
}
}
| Property |
Type |
Description |
kind |
string |
Warning kind |
message |
string |
Warning message |
details |
object |
Additional details |
agent.loop.detected
Emitted when the agent detects a tool-use loop.
{
"id": "...", "ts": "...", "run_id": "...",
"event": "agent.loop.detected",
"node_id": "code", "node_label": "code",
"session_id": "ses_abc",
"properties": {}
}
No properties.
agent.turn.limit
Emitted when the agent reaches its maximum turn count.
{
"id": "...", "ts": "...", "run_id": "...",
"event": "agent.turn.limit",
"node_id": "code", "node_label": "code",
"session_id": "ses_abc",
"properties": {
"max_turns": 25
}
}
| Property |
Type |
Description |
max_turns |
number |
Maximum turns allowed |
agent.skill.expanded
{
"id": "...", "ts": "...", "run_id": "...",
"event": "agent.skill.expanded",
"node_id": "code", "node_label": "code",
"session_id": "ses_abc",
"properties": {
"skill_name": "read_file"
}
}
| Property |
Type |
Description |
skill_name |
string |
Expanded skill name |
agent.steering.injected
{
"id": "...", "ts": "...", "run_id": "...",
"event": "agent.steering.injected",
"node_id": "code", "node_label": "code",
"session_id": "ses_abc",
"properties": {
"text": "Remember to run tests after changes"
}
}
| Property |
Type |
Description |
text |
string |
Injected steering text |
agent.compaction.started
{
"id": "...", "ts": "...", "run_id": "...",
"event": "agent.compaction.started",
"node_id": "code", "node_label": "code",
"session_id": "ses_abc",
"properties": {
"estimated_tokens": 50000,
"context_window_size": 128000
}
}
| Property |
Type |
Description |
estimated_tokens |
number |
Estimated tokens before compaction |
context_window_size |
number |
Model context window size |
agent.compaction.completed
{
"id": "...", "ts": "...", "run_id": "...",
"event": "agent.compaction.completed",
"node_id": "code", "node_label": "code",
"session_id": "ses_abc",
"properties": {
"original_turn_count": 40,
"preserved_turn_count": 10,
"summary_token_estimate": 2000,
"tracked_file_count": 5
}
}
| Property |
Type |
Description |
original_turn_count |
number |
Turns before compaction |
preserved_turn_count |
number |
Turns preserved |
summary_token_estimate |
number |
Token estimate for summary |
tracked_file_count |
number |
Files being tracked |
agent.llm.retry
Emitted when an LLM API call is retried.
{
"id": "...", "ts": "...", "run_id": "...",
"event": "agent.llm.retry",
"node_id": "code", "node_label": "code",
"session_id": "ses_abc",
"properties": {
"provider": "anthropic",
"model": "claude-sonnet-4-20250514",
"attempt": 2,
"delay_secs": 1.5,
"error": { ... }
}
}
| Property |
Type |
Description |
provider |
string |
LLM provider name |
model |
string |
Model identifier |
attempt |
number |
Retry attempt number |
delay_secs |
number |
Delay before retry in seconds |
error |
object |
SdkError (serialized) |
agent.sub.spawned
Emitted when a sub-agent is spawned.
{
"id": "...", "ts": "...", "run_id": "...",
"event": "agent.sub.spawned",
"node_id": "code", "node_label": "code",
"session_id": "ses_abc",
"properties": {
"agent_id": "sub_xyz",
"depth": 1,
"task": "Write unit tests for auth.rs"
}
}
| Property |
Type |
Description |
agent_id |
string |
Sub-agent identifier |
depth |
number |
Nesting depth |
task |
string |
Task description |
agent.sub.completed
{
"id": "...", "ts": "...", "run_id": "...",
"event": "agent.sub.completed",
"node_id": "code", "node_label": "code",
"session_id": "ses_abc",
"properties": {
"agent_id": "sub_xyz",
"depth": 1,
"success": true,
"turns_used": 8
}
}
| Property |
Type |
Description |
agent_id |
string |
Sub-agent identifier |
depth |
number |
Nesting depth |
success |
boolean |
Whether the sub-agent succeeded |
turns_used |
number |
Number of turns used |
agent.sub.failed
{
"id": "...", "ts": "...", "run_id": "...",
"event": "agent.sub.failed",
"node_id": "code", "node_label": "code",
"session_id": "ses_abc",
"properties": {
"agent_id": "sub_xyz",
"depth": 1,
"error": { ... }
}
}
| Property |
Type |
Description |
agent_id |
string |
Sub-agent identifier |
depth |
number |
Nesting depth |
error |
object |
AgentError (serialized) |
agent.sub.closed
{
"id": "...", "ts": "...", "run_id": "...",
"event": "agent.sub.closed",
"node_id": "code", "node_label": "code",
"session_id": "ses_abc",
"properties": {
"agent_id": "sub_xyz",
"depth": 1
}
}
| Property |
Type |
Description |
agent_id |
string |
Sub-agent identifier |
depth |
number |
Nesting depth |
agent.mcp.ready
{
"id": "...", "ts": "...", "run_id": "...",
"event": "agent.mcp.ready",
"node_id": "code", "node_label": "code",
"session_id": "ses_abc",
"properties": {
"server_name": "filesystem",
"tool_count": 5
}
}
| Property |
Type |
Description |
server_name |
string |
MCP server name |
tool_count |
number |
Number of tools available |
agent.mcp.failed
{
"id": "...", "ts": "...", "run_id": "...",
"event": "agent.mcp.failed",
"node_id": "code", "node_label": "code",
"session_id": "ses_abc",
"properties": {
"server_name": "filesystem",
"error": "Connection refused"
}
}
| Property |
Type |
Description |
server_name |
string |
MCP server name |
error |
string |
Error message |
agent.failover
Emitted when the agent fails over to a different LLM provider/model.
{
"id": "...", "ts": "...", "run_id": "...",
"event": "agent.failover",
"node_id": "code",
"node_label": "code",
"properties": {
"from_provider": "anthropic",
"from_model": "claude-sonnet-4-20250514",
"to_provider": "openai",
"to_model": "gpt-4o",
"error": "rate limited"
}
}
| Property |
Type |
Description |
from_provider |
string |
Original provider |
from_model |
string |
Original model |
to_provider |
string |
Failover provider |
to_model |
string |
Failover model |
error |
string |
Error that triggered failover |
Subgraph events
subgraph.started
{
"id": "...", "ts": "...", "run_id": "...",
"event": "subgraph.started",
"node_id": "pipeline",
"node_label": "pipeline",
"properties": {
"start_node": "sub_start"
}
}
| Property |
Type |
Description |
start_node |
string |
First node in the subgraph |
subgraph.completed
{
"id": "...", "ts": "...", "run_id": "...",
"event": "subgraph.completed",
"node_id": "pipeline",
"node_label": "pipeline",
"properties": {
"steps_executed": 4,
"status": "success",
"duration_ms": 25000
}
}
| Property |
Type |
Description |
steps_executed |
number |
Number of steps executed |
status |
string |
Subgraph outcome status |
duration_ms |
number |
Subgraph duration |
Sandbox events
Sandbox events have the nested SandboxEvent unwrapped into properties.
sandbox.initializing
{
"id": "...", "ts": "...", "run_id": "...",
"event": "sandbox.initializing",
"properties": {
"provider": "daytona"
}
}
| Property |
Type |
Description |
provider |
string |
Sandbox provider name |
sandbox.ready
{
"id": "...", "ts": "...", "run_id": "...",
"event": "sandbox.ready",
"properties": {
"provider": "daytona",
"duration_ms": 5000,
"name": "sandbox-01JQXYZ",
"cpu": 4.0,
"memory": 8.0,
"url": "https://sandbox.example.com"
}
}
| Property |
Type |
Description |
provider |
string |
Sandbox provider name |
duration_ms |
number |
Initialization duration |
name |
string? |
Sandbox instance name |
cpu |
number? |
CPU cores allocated |
memory |
number? |
Memory in GB allocated |
url |
string? |
Sandbox URL |
sandbox.failed
{
"id": "...", "ts": "...", "run_id": "...",
"event": "sandbox.failed",
"properties": {
"provider": "daytona",
"error": "workspace creation failed",
"duration_ms": 3000
}
}
| Property |
Type |
Description |
provider |
string |
Sandbox provider name |
error |
string |
Error message |
duration_ms |
number |
Time before failure |
sandbox.initialized
Emitted after the engine completes sandbox initialization (distinct from sandbox.ready which comes from the sandbox provider).
{
"id": "...", "ts": "...", "run_id": "...",
"event": "sandbox.initialized",
"properties": {
"working_directory": "/workspace/my-project",
"provider": "daytona",
"identifier": "sandbox-123",
"host_working_directory": "/tmp/fabro-run/worktree",
"container_mount_point": "/workspace"
}
}
| Property |
Type |
Description |
working_directory |
string |
Working directory inside sandbox |
provider |
string |
Sandbox provider |
identifier |
string? |
Provider-specific sandbox identifier |
host_working_directory |
string? |
Host-side working directory |
container_mount_point |
string? |
Container mount point inside the sandbox |
sandbox.cleanup.started
{
"id": "...", "ts": "...", "run_id": "...",
"event": "sandbox.cleanup.started",
"properties": {
"provider": "daytona"
}
}
| Property |
Type |
Description |
provider |
string |
Sandbox provider name |
sandbox.cleanup.completed
{
"id": "...", "ts": "...", "run_id": "...",
"event": "sandbox.cleanup.completed",
"properties": {
"provider": "daytona",
"duration_ms": 2000
}
}
| Property |
Type |
Description |
provider |
string |
Sandbox provider name |
duration_ms |
number |
Cleanup duration |
sandbox.cleanup.failed
{
"id": "...", "ts": "...", "run_id": "...",
"event": "sandbox.cleanup.failed",
"properties": {
"provider": "daytona",
"error": "workspace not found"
}
}
| Property |
Type |
Description |
provider |
string |
Sandbox provider name |
error |
string |
Error message |
sandbox.snapshot.pulling
{
"id": "...", "ts": "...", "run_id": "...",
"event": "sandbox.snapshot.pulling",
"properties": {
"name": "my-image:latest"
}
}
| Property |
Type |
Description |
name |
string |
Image/snapshot name |
sandbox.snapshot.pulled
{
"id": "...", "ts": "...", "run_id": "...",
"event": "sandbox.snapshot.pulled",
"properties": {
"name": "my-image:latest",
"duration_ms": 15000
}
}
| Property |
Type |
Description |
name |
string |
Image/snapshot name |
duration_ms |
number |
Pull duration |
sandbox.snapshot.ensuring
{
"id": "...", "ts": "...", "run_id": "...",
"event": "sandbox.snapshot.ensuring",
"properties": {
"name": "my-snapshot"
}
}
| Property |
Type |
Description |
name |
string |
Snapshot name |
sandbox.snapshot.creating
{
"id": "...", "ts": "...", "run_id": "...",
"event": "sandbox.snapshot.creating",
"properties": {
"name": "my-snapshot"
}
}
| Property |
Type |
Description |
name |
string |
Snapshot name |
sandbox.snapshot.ready
{
"id": "...", "ts": "...", "run_id": "...",
"event": "sandbox.snapshot.ready",
"properties": {
"name": "my-snapshot",
"duration_ms": 30000
}
}
| Property |
Type |
Description |
name |
string |
Snapshot name |
duration_ms |
number |
Creation duration |
sandbox.snapshot.failed
{
"id": "...", "ts": "...", "run_id": "...",
"event": "sandbox.snapshot.failed",
"properties": {
"name": "my-snapshot",
"error": "disk quota exceeded"
}
}
| Property |
Type |
Description |
name |
string |
Snapshot name |
error |
string |
Error message |
sandbox.git.started
{
"id": "...", "ts": "...", "run_id": "...",
"event": "sandbox.git.started",
"properties": {
"url": "https://github.com/org/repo.git",
"branch": "main"
}
}
| Property |
Type |
Description |
url |
string |
Repository URL |
branch |
string? |
Branch to clone |
sandbox.git.completed
{
"id": "...", "ts": "...", "run_id": "...",
"event": "sandbox.git.completed",
"properties": {
"url": "https://github.com/org/repo.git",
"duration_ms": 8000
}
}
| Property |
Type |
Description |
url |
string |
Repository URL |
duration_ms |
number |
Clone duration |
sandbox.git.failed
{
"id": "...", "ts": "...", "run_id": "...",
"event": "sandbox.git.failed",
"properties": {
"url": "https://github.com/org/repo.git",
"error": "authentication failed"
}
}
| Property |
Type |
Description |
url |
string |
Repository URL |
error |
string |
Error message |
Setup events
setup.started
{
"id": "...", "ts": "...", "run_id": "...",
"event": "setup.started",
"properties": {
"command_count": 3
}
}
| Property |
Type |
Description |
command_count |
number |
Number of setup commands |
setup.command.started
{
"id": "...", "ts": "...", "run_id": "...",
"event": "setup.command.started",
"properties": {
"command": "npm install",
"index": 0
}
}
| Property |
Type |
Description |
command |
string |
Command being run |
index |
number |
Command index |
setup.command.completed
{
"id": "...", "ts": "...", "run_id": "...",
"event": "setup.command.completed",
"properties": {
"command": "npm install",
"index": 0,
"exit_code": 0,
"duration_ms": 5000
}
}
| Property |
Type |
Description |
command |
string |
Command that ran |
index |
number |
Command index |
exit_code |
number |
Process exit code |
duration_ms |
number |
Command duration |
setup.completed
{
"id": "...", "ts": "...", "run_id": "...",
"event": "setup.completed",
"properties": {
"duration_ms": 15000
}
}
| Property |
Type |
Description |
duration_ms |
number |
Total setup duration |
setup.failed
{
"id": "...", "ts": "...", "run_id": "...",
"event": "setup.failed",
"properties": {
"command": "npm install",
"index": 1,
"exit_code": 1,
"stderr": "npm ERR! ..."
}
}
| Property |
Type |
Description |
command |
string |
Command that failed |
index |
number |
Command index |
exit_code |
number |
Process exit code |
stderr |
string |
Standard error output |
CLI ensure events
cli.ensure.started
{
"id": "...", "ts": "...", "run_id": "...",
"event": "cli.ensure.started",
"properties": {
"cli_name": "aider",
"provider": "openai"
}
}
| Property |
Type |
Description |
cli_name |
string |
CLI tool name |
provider |
string |
LLM provider |
cli.ensure.completed
{
"id": "...", "ts": "...", "run_id": "...",
"event": "cli.ensure.completed",
"properties": {
"cli_name": "aider",
"provider": "openai",
"already_installed": true,
"node_installed": false,
"duration_ms": 500
}
}
| Property |
Type |
Description |
cli_name |
string |
CLI tool name |
provider |
string |
LLM provider |
already_installed |
boolean |
Whether it was already present |
node_installed |
boolean |
Whether Node.js was installed |
duration_ms |
number |
Duration |
cli.ensure.failed
{
"id": "...", "ts": "...", "run_id": "...",
"event": "cli.ensure.failed",
"properties": {
"cli_name": "aider",
"provider": "openai",
"error": "pip install failed",
"duration_ms": 3000
}
}
| Property |
Type |
Description |
cli_name |
string |
CLI tool name |
provider |
string |
LLM provider |
error |
string |
Error message |
duration_ms |
number |
Duration |
Pull request events
pull_request.created
{
"id": "...", "ts": "...", "run_id": "...",
"event": "pull_request.created",
"properties": {
"pr_url": "https://github.com/org/repo/pull/42",
"pr_number": 42,
"draft": true
}
}
| Property |
Type |
Description |
pr_url |
string |
Pull request URL |
pr_number |
number |
Pull request number |
draft |
boolean |
Whether the PR is a draft |
pull_request.failed
{
"id": "...", "ts": "...", "run_id": "...",
"event": "pull_request.failed",
"properties": {
"error": "insufficient permissions"
}
}
| Property |
Type |
Description |
error |
string |
Error message |
Devcontainer events
devcontainer.resolved
{
"id": "...", "ts": "...", "run_id": "...",
"event": "devcontainer.resolved",
"properties": {
"dockerfile_lines": 15,
"environment_count": 3,
"lifecycle_command_count": 2,
"workspace_folder": "/workspace"
}
}
| Property |
Type |
Description |
dockerfile_lines |
number |
Lines in generated Dockerfile |
environment_count |
number |
Environment variables defined |
lifecycle_command_count |
number |
Lifecycle commands to run |
workspace_folder |
string |
Workspace folder path |
devcontainer.lifecycle.started
{
"id": "...", "ts": "...", "run_id": "...",
"event": "devcontainer.lifecycle.started",
"properties": {
"phase": "postCreateCommand",
"command_count": 2
}
}
| Property |
Type |
Description |
phase |
string |
Lifecycle phase name |
command_count |
number |
Commands in this phase |
devcontainer.lifecycle.command.started
{
"id": "...", "ts": "...", "run_id": "...",
"event": "devcontainer.lifecycle.command.started",
"properties": {
"phase": "postCreateCommand",
"command": "npm install",
"index": 0
}
}
| Property |
Type |
Description |
phase |
string |
Lifecycle phase name |
command |
string |
Command being run |
index |
number |
Command index |
devcontainer.lifecycle.command.completed
{
"id": "...", "ts": "...", "run_id": "...",
"event": "devcontainer.lifecycle.command.completed",
"properties": {
"phase": "postCreateCommand",
"command": "npm install",
"index": 0,
"exit_code": 0,
"duration_ms": 8000
}
}
| Property |
Type |
Description |
phase |
string |
Lifecycle phase name |
command |
string |
Command that ran |
index |
number |
Command index |
exit_code |
number |
Process exit code |
duration_ms |
number |
Command duration |
devcontainer.lifecycle.completed
{
"id": "...", "ts": "...", "run_id": "...",
"event": "devcontainer.lifecycle.completed",
"properties": {
"phase": "postCreateCommand",
"duration_ms": 12000
}
}
| Property |
Type |
Description |
phase |
string |
Lifecycle phase name |
duration_ms |
number |
Phase duration |
devcontainer.lifecycle.failed
{
"id": "...", "ts": "...", "run_id": "...",
"event": "devcontainer.lifecycle.failed",
"properties": {
"phase": "postCreateCommand",
"command": "npm install",
"index": 0,
"exit_code": 1,
"stderr": "npm ERR! ..."
}
}
| Property |
Type |
Description |
phase |
string |
Lifecycle phase name |
command |
string |
Command that failed |
index |
number |
Command index |
exit_code |
number |
Process exit code |
stderr |
string |
Standard error output |
Asset events
asset.captured
{
"id": "...", "ts": "...", "run_id": "...",
"event": "asset.captured",
"node_id": "code",
"node_label": "code",
"properties": {
"attempt": 1,
"node_slug": "code",
"path": "screenshot.png",
"mime": "image/png",
"content_md5": "d41d8cd98f00b204e9800998ecf8427e",
"content_sha256": "e3b0c44298fc1c149afbf4c8996fb924...",
"bytes": 45000
}
}
| Property |
Type |
Description |
attempt |
number |
Attempt number |
node_slug |
string |
Node slug for asset path |
path |
string |
Asset file path |
mime |
string |
MIME type |
content_md5 |
string |
MD5 hash |
content_sha256 |
string |
SHA-256 hash |
bytes |
number |
File size in bytes |
SSH events
ssh.ready
{
"id": "...", "ts": "...", "run_id": "...",
"event": "ssh.ready",
"properties": {
"ssh_command": "ssh user@host -p 2222"
}
}
| Property |
Type |
Description |
ssh_command |
string |
SSH command to connect |
Watchdog events
watchdog.timeout
Emitted when the stall watchdog detects no progress.
{
"id": "...", "ts": "...", "run_id": "...",
"event": "watchdog.timeout",
"node_id": "code",
"node_label": "code",
"properties": {
"idle_seconds": 1800
}
}
| Property |
Type |
Description |
idle_seconds |
number |
Seconds since last activity |
Retro events
retro.started
{
"id": "...", "ts": "...", "run_id": "...",
"event": "retro.started",
"properties": {
"prompt": "Analyze the workflow run data at `/tmp/retro_data/` ...",
"provider": "anthropic",
"model": "claude-sonnet-4-20250514"
}
}
| Property |
Type |
Description |
prompt |
string? |
Prompt sent to the retro agent |
provider |
string? |
LLM provider for the retro agent |
model |
string? |
Model used for the retro agent |
retro.completed
{
"id": "...", "ts": "...", "run_id": "...",
"event": "retro.completed",
"properties": {
"duration_ms": 5000,
"response": "The run was mostly smooth...",
"retro": {"smoothness": "smooth"}
}
}
| Property |
Type |
Description |
duration_ms |
number |
Retro duration |
response |
string? |
Raw assistant response from the retro agent |
retro |
object? |
Parsed Retro payload |
retro.failed
{
"id": "...", "ts": "...", "run_id": "...",
"event": "retro.failed",
"properties": {
"error": "LLM request failed",
"duration_ms": 3000
}
}
| Property |
Type |
Description |
error |
string |
Error message |
duration_ms |
number |
Duration before failure |