mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-08-28 05:27:41 +00:00
# Conflicts: # lib/foundation/fabro-api/src/lib.rs # lib/foundation/fabro-client/src/client.rs
2285 lines
59 KiB
Markdown
2285 lines
59 KiB
Markdown
# Events
|
|
|
|
Every serialized run event envelope, whether streamed over SSE, returned by `fabro events`, or written to a JSONL sink, uses this structure:
|
|
|
|
```json
|
|
{
|
|
"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.
|
|
|
|
```json
|
|
{
|
|
"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.
|
|
|
|
```json
|
|
{
|
|
"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).
|
|
|
|
```json
|
|
{
|
|
"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.
|
|
|
|
```json
|
|
{
|
|
"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.
|
|
|
|
```json
|
|
{
|
|
"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.
|
|
|
|
```json
|
|
{
|
|
"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.
|
|
|
|
```json
|
|
{
|
|
"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`.
|
|
|
|
```json
|
|
{
|
|
"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.
|
|
|
|
```json
|
|
{
|
|
"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.
|
|
|
|
```json
|
|
{
|
|
"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.
|
|
|
|
```json
|
|
{
|
|
"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.
|
|
|
|
```json
|
|
{
|
|
"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).
|
|
|
|
```json
|
|
{
|
|
"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.
|
|
|
|
```json
|
|
{
|
|
"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.
|
|
|
|
```json
|
|
{
|
|
"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.
|
|
|
|
```json
|
|
{
|
|
"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.
|
|
|
|
```json
|
|
{
|
|
"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.
|
|
|
|
```json
|
|
{
|
|
"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.
|
|
|
|
```json
|
|
{
|
|
"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.
|
|
|
|
```json
|
|
{
|
|
"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.
|
|
|
|
```json
|
|
{
|
|
"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.
|
|
|
|
```json
|
|
{
|
|
"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.
|
|
|
|
```json
|
|
{
|
|
"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.
|
|
|
|
```json
|
|
{
|
|
"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`
|
|
|
|
```json
|
|
{
|
|
"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`
|
|
|
|
```json
|
|
{
|
|
"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`
|
|
|
|
```json
|
|
{
|
|
"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`
|
|
|
|
```json
|
|
{
|
|
"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.
|
|
|
|
```json
|
|
{
|
|
"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.
|
|
|
|
```json
|
|
{
|
|
"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`
|
|
|
|
```json
|
|
{
|
|
"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`
|
|
|
|
```json
|
|
{
|
|
"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`
|
|
|
|
```json
|
|
{
|
|
"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`
|
|
|
|
```json
|
|
{
|
|
"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`
|
|
|
|
```json
|
|
{
|
|
"id": "...", "ts": "...", "run_id": "...",
|
|
"event": "agent.processing.end",
|
|
"node_id": "code", "node_label": "code",
|
|
"session_id": "ses_abc",
|
|
"properties": {}
|
|
}
|
|
```
|
|
|
|
No properties.
|
|
|
|
### `agent.input`
|
|
|
|
```json
|
|
{
|
|
"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.
|
|
|
|
```json
|
|
{
|
|
"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.
|
|
|
|
```json
|
|
{
|
|
"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.
|
|
|
|
```json
|
|
{
|
|
"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.
|
|
|
|
```json
|
|
{
|
|
"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.
|
|
|
|
```json
|
|
{
|
|
"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.
|
|
|
|
```json
|
|
{
|
|
"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.
|
|
|
|
```json
|
|
{
|
|
"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`
|
|
|
|
```json
|
|
{
|
|
"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.
|
|
|
|
```json
|
|
{
|
|
"id": "...", "ts": "...", "run_id": "...",
|
|
"event": "agent.loop.detected",
|
|
"node_id": "code", "node_label": "code",
|
|
"session_id": "ses_abc",
|
|
"properties": {}
|
|
}
|
|
```
|
|
|
|
No properties.
|
|
|
|
### `agent.steering.injected`
|
|
|
|
```json
|
|
{
|
|
"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`
|
|
|
|
```json
|
|
{
|
|
"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`
|
|
|
|
```json
|
|
{
|
|
"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.
|
|
|
|
```json
|
|
{
|
|
"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.
|
|
|
|
```json
|
|
{
|
|
"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`
|
|
|
|
```json
|
|
{
|
|
"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`
|
|
|
|
```json
|
|
{
|
|
"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`
|
|
|
|
```json
|
|
{
|
|
"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`
|
|
|
|
```json
|
|
{
|
|
"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`
|
|
|
|
```json
|
|
{
|
|
"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.
|
|
|
|
```json
|
|
{
|
|
"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.
|
|
|
|
```json
|
|
{
|
|
"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.
|
|
|
|
```json
|
|
{
|
|
"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.
|
|
|
|
```json
|
|
{
|
|
"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`
|
|
|
|
```json
|
|
{
|
|
"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`
|
|
|
|
```json
|
|
{
|
|
"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`
|
|
|
|
```json
|
|
{
|
|
"id": "...", "ts": "...", "run_id": "...",
|
|
"event": "sandbox.initializing",
|
|
"properties": {
|
|
"provider": "daytona"
|
|
}
|
|
}
|
|
```
|
|
|
|
| Property | Type | Description |
|
|
|----------|------|-------------|
|
|
| `provider` | string | Sandbox provider name |
|
|
|
|
### `sandbox.ready`
|
|
|
|
```json
|
|
{
|
|
"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`
|
|
|
|
```json
|
|
{
|
|
"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).
|
|
|
|
```json
|
|
{
|
|
"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`
|
|
|
|
```json
|
|
{
|
|
"id": "...", "ts": "...", "run_id": "...",
|
|
"event": "sandbox.cleanup.started",
|
|
"properties": {
|
|
"provider": "daytona"
|
|
}
|
|
}
|
|
```
|
|
|
|
| Property | Type | Description |
|
|
|----------|------|-------------|
|
|
| `provider` | string | Sandbox provider name |
|
|
|
|
### `sandbox.cleanup.completed`
|
|
|
|
```json
|
|
{
|
|
"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`
|
|
|
|
```json
|
|
{
|
|
"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.
|
|
|
|
```json
|
|
{
|
|
"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.
|
|
|
|
```json
|
|
{
|
|
"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.
|
|
|
|
```json
|
|
{
|
|
"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.
|
|
|
|
```json
|
|
{
|
|
"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`
|
|
|
|
```json
|
|
{
|
|
"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`
|
|
|
|
```json
|
|
{
|
|
"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`
|
|
|
|
```json
|
|
{
|
|
"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`
|
|
|
|
```json
|
|
{
|
|
"id": "...", "ts": "...", "run_id": "...",
|
|
"event": "setup.started",
|
|
"properties": {
|
|
"command_count": 3
|
|
}
|
|
}
|
|
```
|
|
|
|
| Property | Type | Description |
|
|
|----------|------|-------------|
|
|
| `command_count` | number | Number of setup commands |
|
|
|
|
### `setup.command.started`
|
|
|
|
```json
|
|
{
|
|
"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`
|
|
|
|
```json
|
|
{
|
|
"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`
|
|
|
|
```json
|
|
{
|
|
"id": "...", "ts": "...", "run_id": "...",
|
|
"event": "setup.completed",
|
|
"properties": {
|
|
"duration_ms": 15000
|
|
}
|
|
}
|
|
```
|
|
|
|
| Property | Type | Description |
|
|
|----------|------|-------------|
|
|
| `duration_ms` | number | Total setup duration |
|
|
|
|
### `setup.failed`
|
|
|
|
```json
|
|
{
|
|
"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`
|
|
|
|
```json
|
|
{
|
|
"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`
|
|
|
|
```json
|
|
{
|
|
"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`
|
|
|
|
```json
|
|
{
|
|
"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`
|
|
|
|
```json
|
|
{
|
|
"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`
|
|
|
|
```json
|
|
{
|
|
"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`
|
|
|
|
```json
|
|
{
|
|
"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`
|
|
|
|
```json
|
|
{
|
|
"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`
|
|
|
|
```json
|
|
{
|
|
"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`
|
|
|
|
```json
|
|
{
|
|
"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`
|
|
|
|
```json
|
|
{
|
|
"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.
|
|
|
|
```json
|
|
{
|
|
"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 |
|