Lifecycle & Events
Distri emits structured events for every agent run. Use them to drive UI updates, telemetry, and workflow automation.

Run Lifecycle
| Event | Payload | Notes |
|---|---|---|
run_started | { thread_id, run_id, agent_id } | Fired when a new run begins. |
plan_started | { initial_plan } | Indicates the planner kicked off. |
plan_finished | { total_steps } | Planner produced a plan. |
plan_pruned | { removed_steps } | Planner removed branches. |
run_finished | { success, total_steps, failed_steps } | Run completed successfully. |
run_error | { message, code? } | Terminal failure. |
Step Execution
| Event | Payload | Notes |
|---|---|---|
step_started | { step_id, step_index } | Planner step begins executing. |
step_completed | { step_id, success } | Step finished, identify retries/flaky tools. |
Tool Execution
| Event | Payload | Notes |
|---|---|---|
tool_execution_start | { step_id, tool_call_id, tool_call_name, input } | Tool call dispatched. |
tool_execution_end | { step_id, tool_call_id, tool_call_name, success } | Tool finished; check success. |
tool_calls | { step_id, parent_message_id?, tool_calls: ToolCall[] } | Batched tool call metadata. |
tool_results | { step_id, parent_message_id?, results: ToolResponse[] } | Batched tool outputs. |
Streaming Messages
| Event | Payload | Notes |
|---|---|---|
text_message_start | { message_id, step_id, role, is_final? } | Begin streaming content for a message. |
text_message_content | { message_id, step_id, delta, stripped_content? } | Token-level updates. |
text_message_end | { message_id, step_id } | Message finished streaming. |
Agent Handover
| Event | Payload | Notes |
|---|---|---|
agent_handover | { from_agent, to_agent, reason? } | Control transferred to another agent. |
Workflow Orchestration
| Event | Payload | Notes |
|---|---|---|
workflow_started | { workflow_name, total_steps } | Workflow initiated. |
node_started | { node_id, node_name, step_type } | Workflow node began execution. |
node_completed | { node_id, node_name, success, error? } | Node finished with status. |
run_completed | { workflow_name, success, total_steps } | Workflow run succeeded. |
run_failed | { workflow_name, error, failed_at_step? } | Workflow run failed. |
Todos
| Event | Payload | Notes |
|---|---|---|
todos_updated | { formatted_todos, action, todo_count } | Planner-generated tasks changed. |
Listening to Events
React
import { useAgent, useAgentEvents } from '@distri/react';
function AgentUI() {
const { agent } = useAgent({ agentIdOrDef: 'my_agent' });
useAgentEvents({
onRunStarted: (event) => {
console.log('Run started:', event.run_id);
},
onToolExecutionStart: (event) => {
console.log('Tool called:', event.tool_call_name);
},
onTextMessageContent: (event) => {
console.log('Streaming:', event.delta);
},
onRunFinished: (event) => {
console.log('Run finished:', event.success);
},
});
return <Chat agent={agent} />;
}
SSE (Server-Sent Events)
const eventSource = new EventSource(
'https://api.distri.dev/v1/agents/my_agent/stream'
);
eventSource.onmessage = (event) => {
const data = JSON.parse(event.data);
switch (data.type) {
case 'run_started':
console.log('Run started:', data.payload);
break;
case 'tool_execution_start':
console.log('Tool called:', data.payload.tool_call_name);
break;
case 'text_message_content':
console.log('Content:', data.payload.delta);
break;
}
};
Creating Custom Events
use distri_types::events::{AgentEvent, AgentEventType};
let event = AgentEvent::with_context(
AgentEventType::RunError {
message: "verification failed".into(),
code: Some("INVALID_STATE".into()),
},
thread_id.to_string(),
run_id.to_string(),
task_id.to_string(),
agent_id.to_string(),
);
log::info!("event={:?}", event);