fabro/docs/internal/events.md
Bryan Helmkamp 6dfe1c49d2
Merge remote-tracking branch 'origin/main' into feat/async-pr-create
# Conflicts:
#	lib/foundation/fabro-api/src/lib.rs
#	lib/foundation/fabro-client/src/client.rs
2026-08-04 15:04:24 -04:00

59 KiB

Events

Every serialized run event envelope, whether streamed over SSE, returned by fabro events, 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.created

Emitted when the run record is created.

{
  "id": "...", "ts": "...", "run_id": "...",
  "event": "run.created",
  "properties": {
    "workflow_slug": "my-workflow",
    "source_directory": "/home/user/src/my-project",
    "git": {
      "origin_url": "https://github.com/acme/my-project",
      "branch": "main",
      "sha": "abc123",
      "dirty": "clean"
    },
    "fork_source_ref": null,
    "in_place": false,
    "provenance": {
      "subject": {
        "kind": "user",
        "identity": {
          "issuer": "https://github.com",
          "subject": "12345"
        },
        "login": "octocat",
        "auth_method": "github"
      }
    }
  }
}
Property Type Description
settings object Workflow settings snapshot
graph object Parsed workflow graph
workflow_source string? Workflow source text
labels object Run labels
source_directory string? Submitter-side source directory
workflow_slug string? Workflow slug
provenance object Actor and request provenance
manifest_blob string? Blob id for the submitted manifest
git object? Git provenance observed before the run: normalized origin_url, branch, optional sha, and dirty status
fork_source_ref object? Source run/checkpoint reference when this run was forked
in_place boolean Whether the run was created with --in-place (no git checkpoints)

Readers remain tolerant of the legacy workflow_config, run_dir, and db_prefix properties, and of a legacy push_outcome object nested inside git, when replaying historical events; newly emitted run.created events omit them.

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": "succeeded",
    "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 stage outcome ("succeeded", "failed", "partially_succeeded", "skipped")
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

run.interrupt

Emitted after a live worker accepts a run interrupt control operation. The actor is stored in the top-level actor envelope field. Properties are empty.

{
  "id": "...", "ts": "...", "run_id": "...",
  "event": "run.interrupt",
  "actor": { "kind": "user", "login": "octocat" },
  "properties": {}
}

run.steer

Emitted after a live worker accepts run steering text. The actor is stored in the top-level actor envelope field.

{
  "id": "...", "ts": "...", "run_id": "...",
  "event": "run.steer",
  "actor": { "kind": "user", "login": "octocat" },
  "properties": {
    "text": "Remember to run tests after changes"
  }
}
Property Type Description
text string Accepted steering text

metadata.snapshot.started

Emitted when Fabro begins a durable metadata snapshot operation. These are product events for Fabro metadata snapshots, not tracing spans for the underlying git or filesystem work.

Init and finalize metadata snapshots are unscoped. Checkpoint metadata snapshots use the checkpoint stage scope, so they include the checkpoint node_id, node_label, and stage_id.

{
  "id": "...", "ts": "...", "run_id": "...",
  "event": "metadata.snapshot.started",
  "properties": {
    "phase": "checkpoint",
    "branch": "fabro/meta"
  }
}
Property Type Description
phase string Logical metadata operation: "init", "checkpoint", or "finalize"
branch string Metadata branch/ref being written

metadata.snapshot.completed

Emitted when Fabro commits and pushes a metadata snapshot successfully.

{
  "id": "...", "ts": "...", "run_id": "...",
  "event": "metadata.snapshot.completed",
  "properties": {
    "phase": "checkpoint",
    "branch": "fabro/meta",
    "duration_ms": 2800,
    "entry_count": 12,
    "bytes": 18432,
    "commit_sha": "def456..."
  }
}
Property Type Description
phase string Logical metadata operation: "init", "checkpoint", or "finalize"
branch string Metadata branch/ref that was written
duration_ms number End-to-end duration of the metadata snapshot operation
entry_count number Number of metadata files written into the snapshot commit
bytes number Sum of serialized metadata entry byte lengths
commit_sha string Metadata snapshot commit SHA

metadata.snapshot.failed

Emitted when a real metadata snapshot attempt fails. It is emitted before the matching compatibility run.notice, allowing human-facing consumers to suppress duplicate warning text. Compatibility notices with codes checkpoint_metadata_write_failed and checkpoint_metadata_push_failed may still appear in raw event streams. The checkpoint_metadata_degraded notice is a separate summary signal and should not be treated as a duplicate of this event.

{
  "id": "...", "ts": "...", "run_id": "...",
  "event": "metadata.snapshot.failed",
  "properties": {
    "phase": "checkpoint",
    "branch": "fabro/meta",
    "duration_ms": 900,
    "failure_kind": "push",
    "error": "failed to push metadata snapshot",
    "causes": ["remote rejected the push"],
    "commit_sha": "def456...",
    "entry_count": 12,
    "bytes": 18432
  }
}
Property Type Description
phase string Logical metadata operation: "init", "checkpoint", or "finalize"
branch string Metadata branch/ref being written
duration_ms number End-to-end duration before failure
failure_kind string Failure phase: "load_state", "write", or "push"
error string Primary error summary
causes string[] Error cause chain; omitted when empty
commit_sha string? Local metadata commit SHA for push failures; omitted for load-state and write failures
entry_count number? Metadata entry count for push failures; omitted for load-state and write failures
bytes number? Serialized metadata byte count for push failures; omitted for load-state and write failures

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": "succeeded",
    "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 "succeeded", "failed", "skipped", "partially_succeeded"
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? Context snapshot after the stage, minus runtime-only keys such as current.preamble. Artifact pointers are not normalized to blob refs — use checkpoint.completed for the durable projection
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": {
    "visit": 1,
    "branch_count": 3
  }
}
Property Type Description
visit number Visit number for this parallel stage
branch_count number Number of parallel branches

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": "succeeded"
  }
}
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": {
    "visit": 1,
    "duration_ms": 12000,
    "success_count": 2,
    "failure_count": 1,
    "results": [
      {
        "id": "branch_a",
        "status": "succeeded",
        "context_updates": {"response.branch_a": "review complete"}
      },
      {
        "id": "branch_b",
        "status": "failed",
        "context_updates": {"command.output": "validation failed"}
      }
    ]
  }
}
Property Type Description
visit number Visit number for this parallel stage
duration_ms number Total parallel duration
success_count number Branches that succeeded
failure_count number Branches that failed
results array Ordered typed branch results with id, status, and isolated context_updates

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": "succeeded",
    "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.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=succeeded",
    "reason": "condition",
    "preferred_label": "tests_pass",
    "suggested_next_ids": ["review"],
    "stage_status": "succeeded",
    "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

Most agent activity events are stage-scoped and carry node_id (the workflow stage), node_label, stage_id, session_id, and parent_session_id in the envelope. Session object lifecycle events are the exception: agent.session.started and agent.session.ended are not stage-scoped and intentionally omit node_id, node_label, stage_id, and visit.

agent.session.started

{
  "id": "...", "ts": "...", "run_id": "...",
  "event": "agent.session.started",
  "session_id": "ses_abc", "parent_session_id": null,
  "properties": {
    "provider": "openai",
    "model": "gpt-5.4"
  }
}

Object-lifecycle event. session_id and parent_session_id are envelope fields. properties.provider and properties.model are optional.

agent.session.activated

{
  "id": "...", "ts": "...", "run_id": "...",
  "event": "agent.session.activated",
  "node_id": "code", "node_label": "code", "stage_id": "code@1",
  "session_id": "ses_abc",
  "properties": {
    "thread_id": "main",
    "provider": "openai",
    "model": "gpt-5.4",
    "capabilities": ["steer"],
    "visit": 1
  }
}

Stage-scoped lease event. A stage is steerable while the latest matching agent.session.activated lease is active.

agent.session.deactivated

{
  "id": "...", "ts": "...", "run_id": "...",
  "event": "agent.session.deactivated",
  "node_id": "code", "node_label": "code", "stage_id": "code@1",
  "session_id": "ses_abc",
  "properties": { "visit": 1 }
}

Stage-scoped lease event. Consumers should pair it by stage_id and session_id so stale deactivations cannot clear a newer active lease.

agent.session.ended

{
  "id": "...", "ts": "...", "run_id": "...",
  "event": "agent.session.ended",
  "session_id": "ses_abc",
  "properties": {}
}

Object-lifecycle event. session_id and parent_session_id are envelope fields. 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.llm.started

An inference request is about to be dispatched for this round. Emitted once per round, after the request is built and compaction has run, immediately before the stream is opened.

requested_model is the canonical requested target, including an optional speed tier. Failover can re-target mid-stage, so agent.message remains authoritative for what actually answered. No usage or cost fields: neither exists yet at this point.

{
  "id": "...", "ts": "...", "run_id": "...",
  "event": "agent.llm.started",
  "node_id": "code", "node_label": "code",
  "session_id": "ses_abc",
  "properties": {
    "requested_model": {
      "provider": "anthropic",
      "model_id": "claude-fable-5",
      "speed": "fast"
    },
    "visit": 1
  }
}
Property Type Description
requested_model object Requested provider, model ID, and optional speed tier
visit number Graph visit

agent.llm.first_output

The provider produced its first output for the current attempt. Edge-triggered once per stream attempt; the latch re-arms when a broken or finish-less stream replays the turn.

{
  "id": "...", "ts": "...", "run_id": "...",
  "event": "agent.llm.first_output",
  "node_id": "code", "node_label": "code",
  "session_id": "ses_abc",
  "properties": {
    "kind": "reasoning",
    "visit": 1
  }
}
Property Type Description
kind string reasoning, text, or tool_call — observed, not inferred
visit number Graph visit

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.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.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.tool.process.completed

Subordinate diagnostic for a tool call that ran a process, emitted between agent.tool.started and agent.tool.completed. It explains the underlying process outcome; agent.tool.completed.is_error remains the protocol and UI truth. Absent when the tool never produced a process result (setup, transport, or launch failure) and when the tool ran without a session-bound emitter.

{
  "id": "...", "ts": "...", "run_id": "...",
  "event": "agent.tool.process.completed",
  "node_id": "code", "node_label": "code",
  "session_id": "ses_abc",
  "tool_call_id": "call_abc123",
  "properties": {
    "exit_code": 7,
    "termination": "exited",
    "duration_ms": 812,
    "streams_separated": true,
    "exec_output_tail": {"stdout": "...", "stderr": "..."},
    "visit": 1
  }
}
Property Type Description
exit_code integer Process exit code; omitted for timeout and cancellation
termination string exited, timed_out, or cancelled
duration_ms integer Process duration
streams_separated boolean false when the provider could not separate stdout from stderr; the combined output is then in exec_output_tail.stdout
exec_output_tail object Bounded, redacted output tails; omitted when both streams were empty
exec_output_tail.stdout string Bounded stdout tail, or combined-output tail when streams_separated is false; omitted when empty
exec_output_tail.stderr string Bounded stderr tail; omitted when empty
exec_output_tail.stdout_truncated boolean true when earlier stdout bytes were omitted; omitted when false
exec_output_tail.stderr_truncated boolean true when earlier stderr bytes were omitted; omitted when false
visit integer Stage visit

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.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 attempt fails to open or sustain a stream and the turn is replayed. The finish-less-stream case carries a synthetic Stream error and a zero delay: the turn restarts even though no error was reported.

{
  "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,
    "phase": "open",
    "error": { ... }
  }
}
Property Type Description
provider string LLM provider name
model string Model identifier
attempt number Retry attempt number, 0-based within the loop named by phase
delay_secs number Delay before retry in seconds
phase string? open (stream failed to open) or consume (stream broke or ended without a finish event). Absent on events stored before the discriminator existed
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": "github",
    "tool_count": 2,
    "tools": [
      {
        "name": "mcp__github__create_issue",
        "original_name": "create_issue"
      },
      {
        "name": "mcp__github__list_issues",
        "original_name": "list_issues"
      }
    ],
    "visit": 1
  }
}
Property Type Description
server_name string MCP server name
tool_count number Number of tools available
tools array Names-only tool summaries for the ready server, sorted by qualified name. Each entry has name (Fabro-qualified mcp__{server}__{tool} identifier) and original_name (server-provided tool name). Descriptions and input schemas are intentionally omitted. The field is omitted from serialized JSON for legacy parity when empty.
visit number Stage visit count when the server became ready

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.memory.loaded

Emitted once per session right after memory discovery, before skills and MCP initialization. The event is always emitted, even when no memory files are loaded (in which case files is an empty array). Memory file contents are deliberately excluded from the payload to keep the durable event stream free of project documentation bytes; consumers that need contents must read the files themselves.

{
  "id": "...", "ts": "...", "run_id": "...",
  "event": "agent.memory.loaded",
  "node_id": "code", "node_label": "code",
  "session_id": "ses_abc",
  "properties": {
    "provider_profile": "anthropic",
    "files": [
      {
        "path": "/repo/AGENTS.md",
        "byte_count": 4096,
        "loaded_bytes": 4096,
        "truncated": false
      }
    ],
    "total_loaded_bytes": 4096,
    "budget_bytes": 32768,
    "visit": 1
  }
}
Property Type Description
provider_profile string Active agent profile (anthropic, openai, gemini)
files array Discovered memory files. Empty when no memory was loaded.
files[].path string Absolute path of the memory file in the sandbox
files[].byte_count number Original file size in bytes
files[].loaded_bytes number Bytes actually loaded into the prompt budget
files[].truncated boolean true if the file was truncated to fit the budget
total_loaded_bytes number Sum of files[].loaded_bytes
budget_bytes number Total memory budget for the session (currently 32 KiB)
visit number Stage visit count

agent.skills.discovered

Emitted once per session right after skill discovery completes. The event is always emitted, even when no skills are found (skills is an empty array). Skills are sorted by name. source_dirs lists the directories that were scanned in the configured precedence order.

{
  "id": "...", "ts": "...", "run_id": "...",
  "event": "agent.skills.discovered",
  "node_id": "code", "node_label": "code",
  "session_id": "ses_abc",
  "properties": {
    "provider_profile": "anthropic",
    "source_dirs": [
      "/home/test/.fabro/skills",
      "/repo/.fabro/skills",
      "/repo/skills"
    ],
    "skills": [
      { "name": "commit", "description": "Make a commit" }
    ],
    "visit": 1
  }
}
Property Type Description
provider_profile string Active agent profile
source_dirs array Directories scanned for SKILL.md files (in precedence order)
skills array Discovered skills, sorted by name. Each entry is { name, description }.
visit number Stage visit count

agent.skill.activated

Emitted whenever a skill is activated in the running session. Sources:

  • slash — the user input matched a /skill-name token and the skill template was expanded inline. This event replaces the previous internal-only agent.skill.expanded notification.
  • tool — the model successfully called the use_skill tool and the skill template was returned. Failed use_skill lookups (unknown names, missing parameters) do not emit this event.
{
  "id": "...", "ts": "...", "run_id": "...",
  "event": "agent.skill.activated",
  "node_id": "code", "node_label": "code",
  "session_id": "ses_abc",
  "properties": {
    "skill_name": "commit",
    "source": "slash",
    "visit": 1
  }
}
Property Type Description
skill_name string Name of the activated skill
source string "slash" for /skill-name expansion, "tool" for use_skill activations
visit number Stage visit count

agent.skill.expanded does not exist. The AgentEvent::SkillExpanded variant this note once described has since been removed from the code entirely; slash-skill expansion is reported through agent.skill.activated with source == "slash" instead.

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

Agent events that are never serialized

AgentEvent also has variants that exist only on the agent session's in-process broadcast channel. is_streaming_noise() filters them out before the workflow emitter builds a RunEvent, so they never reach the run store, SSE, fabro events, or a JSONL sink — they have no envelope, and no external consumer can observe them:

  • AssistantOutputReplace — clears in-progress output buffers when a turn is replayed
  • TextDelta, ReasoningDelta — streaming assistant chunks
  • ToolCallOutputDelta — streaming tool output chunks

They were previously documented here as though they were durable events, with full envelope examples. If any of them ever needs to be durable, it belongs in a separate transient stream rather than the canonical persisted contract — long autonomous runs would generate orders of magnitude more delta traffic than the interactive sessions surface handles.


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": "succeeded",
    "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",
    "repo_cloned": true,
    "clone_origin_url": "https://github.com/acme/my-project.git",
    "clone_branch": "main"
  }
}
Property Type Description
working_directory string Working directory inside sandbox
provider string Sandbox provider
identifier string? Provider-specific sandbox identifier
repo_cloned boolean? Whether the provider cloned a repository into the sandbox
clone_origin_url string? Repository URL cloned into the sandbox, with credentials removed
clone_branch string? Branch requested for the sandbox clone

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

Emitted only when the Docker image cache misses and Fabro starts pulling the image.

{
  "id": "...", "ts": "...", "run_id": "...",
  "event": "sandbox.snapshot.pulling",
  "properties": {
    "name": "my-image:latest"
  }
}
Property Type Description
name string Image/snapshot name

sandbox.snapshot.creating

Emitted only when a Daytona snapshot cache miss or inactive snapshot requires Fabro to create or wait for the snapshot.

{
  "id": "...", "ts": "...", "run_id": "...",
  "event": "sandbox.snapshot.creating",
  "properties": {
    "name": "my-snapshot"
  }
}
Property Type Description
name string Snapshot name

sandbox.snapshot.ready

Emitted when an image or snapshot ensure step succeeds. Cache hits still emit this event with a near-zero duration_ms; explicit no-op paths such as Docker auto_pull = false and the Daytona default snapshot path do not.

{
  "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 Ensure duration

sandbox.snapshot.failed

Emitted when an image or snapshot ensure step fails.

{
  "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
causes string[] Optional error cause chain

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

These legacy events may appear in older run logs. Current CLI backend runs do not emit them because Fabro no longer installs or prepares provider CLIs at stage runtime.

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.creation_requested

{
  "id": "...", "ts": "...", "run_id": "...",
  "event": "pull_request.creation_requested",
  "properties": {
    "creation_id": "01KYYK70WTZT2E551P3H5P0059",
    "model": "gpt-5.4",
    "force": false
  }
}
Property Type Description
creation_id string Stable identifier for this pull request creation request
model string Resolved model identifier used to generate the pull request content
force boolean Whether creation is allowed for a run without a successful conclusion

pull_request.created

{
  "id": "...", "ts": "...", "run_id": "...",
  "event": "pull_request.created",
  "properties": {
    "pr_url": "https://github.com/org/repo/pull/42",
    "pr_number": 42,
    "head_sha": "d34db33f",
    "draft": true
  }
}
Property Type Description
pr_url string Pull request URL
pr_number number Pull request number
head_sha string (optional) Verified commit SHA at the remote PR head; absent on older events
draft boolean Whether the PR is a draft

pull_request.linked

{
  "id": "...", "ts": "...", "run_id": "...",
  "event": "pull_request.linked",
  "properties": {
    "pull_request": {
      "provider": "github",
      "html_url": "https://github.com/org/repo/pull/42",
      "number": 42,
      "owner": "org",
      "repo": "repo",
      "title": "Review deployment chart"
    }
  }
}
Property Type Description
pull_request object Stored GitHub pull request association. title, base_branch, and head_branch may be included when live GitHub metadata is available.

pull_request.unlinked

{
  "id": "...", "ts": "...", "run_id": "...",
  "event": "pull_request.unlinked",
  "properties": {
    "pull_request": {
      "provider": "github",
      "html_url": "https://github.com/org/repo/pull/42",
      "number": 42
    }
  }
}
Property Type Description
pull_request object Pull request association removed from the run.

pull_request.failed

{
  "id": "...", "ts": "...", "run_id": "...",
  "event": "pull_request.failed",
  "properties": {
    "creation_id": "01KYYK70WTZT2E551P3H5P0059",
    "error": "insufficient permissions"
  }
}
Property Type Description
creation_id string (optional) Explicit pull request creation this failure resolves. Absent for publish-stage failures.
error string Error message

When creation_id names the run's pending pull request creation, the run projection marks that creation failed. A pull_request.failed event without a creation_id (the workflow publish stage) does not change creation state.

Artifact events

artifact.captured

{
  "id": "...", "ts": "...", "run_id": "...",
  "event": "artifact.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