Event types
Every AgentEvent has:
@dataclass
class AgentEvent:
type: str # event type name, e.g. "tool_called"
message: str # human-readable label
payload: dict # event-specific fieldsFull event reference
run_started
Emitted once at the very start of a run.
Payload:
prompt: str— the user's input prompt
mcp_attached
Emitted once per attached MCP server, after run_started.
Payload:
server: str— MCP server name
planning_started
Emitted if router_policy.should_plan(prompt) returns True. The plan_task tool runs immediately after.
Payload:
prompt: str
planning_completed
The planner's output is injected into message history as a user-role context message (not as a tool-role result, to preserve Bedrock tool-pairing).
Payload:
output: str— the planner's text output
step_started
Each iteration of the tool loop, right before llm.complete() is called.
Payload:
iteration: int— 1-indexed iteration numbertool_count: int— number of tool schemas sent to the LLM
reasoning_started
🧠 The LLM response contained non-empty reasoning / thinking content.
Payload:
iteration: int
reasoning_completed
Carries the full reasoning text. Always immediately follows reasoning_started.
Payload:
iteration: intcontent: str— the reasoning text
tool_called
The model decided to call a tool. Fires before execution.
Message: "Tool called: <name>"
Payload:
tool: str— tool name (v1.0.15)call_id: str— correlates with the matchingtool_completed/tool_failed(v1.0.15)iteration: intarguments: dict— tool arguments as parsed from the LLM
tool_completed
Tool finished successfully.
Message: "Tool completed: <name>"
Payload:
tool: str— tool name (v1.0.15)call_id: str— matches the originatingtool_called(v1.0.15)iteration: intoutput: str— tool output textduration_ms: float— execution time (v1.0.15)
text_delta
Each chunk of the answer as the model generates it. Concatenate the chunks to
render the reply token by token. (v1.7)
Payload:
chunk: str— the newly generated fragment of text
tool_input_started / tool_input_delta
A tool's arguments streaming in before the call runs — started opens the
stream, each delta carries a fragment. (v1.7)
Payload:
call_id: str,tool: strdelta: str— a fragment of the arguments (tool_input_deltaonly)field: str— the argument being filled, when known
tool_output_started / tool_output_delta
A tool's output streaming as it is produced — the same pattern as input, for results. Render it under the tool's card in real time. (v1.7)
Payload:
call_id: str,tool: strdelta: str— a fragment of the output (tool_output_deltaonly)
tool_arguments_rejected
A tool call was refused before it ran because its arguments were invalid — for example a call that asks for everything. The run continues; surface it so a rejected step is visible rather than silent. (v1.7)
Payload:
tool: str— the tool whose call was rejectedarguments: dict— the arguments that were refusediteration: int
tool_call_healed
A malformed or text-shaped tool call was promoted to a real one by
heal_tool_calls — the safety net weaker models lean on. (v1.7)
Payload:
tools: list[str]— the tool name(s) recoveredhealed_from: str— where the call was recovered from (e.g."content")
run_summary
An end-of-run narration written by the decision model, when progress narration is enabled. (v1.7)
Payload:
summary: str— the narration textheadline: str— a one-line version
final_answer
The finished answer, emitted just before the run closes. run_completed also
carries it; this fires a beat earlier so a client can commit the answer even if
the connection drops between the two. (v1.7)
Payload:
content: str— the final answerformat: str— e.g."markdown"
tool_retry
Transient tool failure, retry scheduled by RetryPolicy.
Payload:
iteration: intattempt: int— retry attempt number (1-indexed)error: str
tool_failed
Non-retryable tool error, or the model hallucinated an unregistered tool name. In the second case, a synthetic "Error: tool X is not registered" tool-result message is still appended to keep pairing balanced.
Payload:
tool: str— tool name (v1.0.15)call_id: str— matches the originatingtool_called(v1.0.15)iteration: interror: strduration_ms: float— time until failure (v1.0.15)
context_compacted
Older turns were condensed to stay inside the configured context window (v1.0.15). Fires at most once per iteration, before the LLM call.
Message: "Older turns condensed to stay within the context window"
Payload:
before: int— message count before compactionafter: int— message count afteriteration: int
llm_retry
Transient LLM provider error, retry scheduled.
Payload:
attempt: interror: str
interactive_request
A tool returned metadata.interactive=True. Your UI can pause and collect input.
Payload:
kind: str— e.g."ask_user","human_review"payload: dict— tool metadata (usually contains the question and expected response format)
run_completed
Final event. Fires once the loop exits or hits the iteration cap.
Payload:
output: str— final answer (legacy name)content: str— final answer (explicit name)format: str— output format, e.g."markdown"
Serialization
All events serialize cleanly to JSON via event.to_dict():
{
"type": "tool_called",
"message": "Tool called: web_search",
"payload": {
"iteration": 1,
"arguments": {"query": "bitcoin price"}
}
}Related
- Streaming guide — high-level usage
- Architecture — how events fit into the runtime