Agent events
Agent events answer more than “what happened”: before a side effect occurs, they can also decide whether it should happen at all. Every Agent’s EventBus supports both observation and synchronous intervention.
Three kinds of data
Section titled “Three kinds of data”| Kind | Purpose | How to get it | Can it intervene? |
|---|---|---|---|
AgentEvent |
Coordinate and intercept the current execution | agent.events.on(...) |
Yes: modify fields or set decision |
StreamEvent |
Deliver output to the caller | Iterate the stream from agent.run() |
Yes: closing the stream ends the Run |
TapeEntry |
Keep the history | Query the Tape | No: normal recording only appends |
The three never convert into each other automatically: stream output is not written to disk on its own, and Tape entries do not flow back into the Step that is running. Only the entries TapeContext selects become input to later model calls.
Synchronous delivery
Section titled “Synchronous delivery”emit() calls each matching handler in registration order, and waits for an async handler to finish before it continues. Four rules are worth remembering:
- A handler registered for a base class also receives its subclasses.
- Return values are ignored; to steer execution, change event fields or set
decision. - Exceptions propagate to the publisher and may fail the whole Run.
event.stoppedis checked before every handler, andTrueends delivery for that emit.
Decisions come before side effects
Section titled “Decisions come before side effects”Before-call events expose a replaceable request and a decision. One tool call has four possible outcomes:
| Action | Does the original tool run? | What the model receives |
|---|---|---|
No decision |
Yes | The real result |
Replace event.call |
Yes, with the new call | The real result |
deny(message) |
No | A tool error |
replace(result) |
No | The given result |
A denied or replaced call still emits ToolCallFinished, so logic that observes completed calls never misses it. Model calls follow the same before-call intervention model: for example, LLMCallDecision.finish(text) can produce text without calling the provider.
Division of labor with App hooks
Section titled “Division of labor with App hooks”Agent events intervene in the model calls, tool calls, Steps, and Runs of the core loop; application hooks cover the application lifecycle — Turn stages, tool supply, storage, and channels — and carry plugin distribution. Application interception hooks can join the same set of Agent events through an adapter, but the two differ in how they return arguments and in their exception policy, as the Hooks reference details. For how to write each, see SDK: events and Plugins and Hooks: intercept a model or tool call.