Skip to main content

Observing Agent events

run.Wait answers one question: how did the run end. Agent events answer a different one: what is happening right now. Events serve the screen, Wait serves the application, and the two never conflict. Deltas stay best-effort, while agent.Result.Final and agent.Result.Context are authoritative.

Picking the events to display

Most interfaces need only a few event types.

Display needEvent
text as it arrivesagent.MessageUpdateEvent carrying generation.TextDeltaEvent
reasoning as it arrivesagent.MessageUpdateEvent carrying generation.ThinkingDeltaEvent
tool activityagent.ToolExecutionStartEvent, ToolExecutionUpdateEvent, and ToolExecutionEndEvent
summary activityagent.CompactionStartEvent and agent.CompactionEndEvent
run completionagent.RunEndEvent, then Wait

Consuming the stream

Start the run, iterate its event stream, then call Wait when the iteration ends.

run, err := assistant.Start(ctx, input)
if err != nil {
return err
}

for event := range run.Events() {
switch event := event.(type) {
case agent.MessageUpdateEvent:
switch update := event.AssistantMessageEvent.(type) {
case generation.TextDeltaEvent:
fmt.Fprint(os.Stderr, update.Delta)
case generation.ThinkingDeltaEvent:
fmt.Fprint(os.Stderr, update.Delta)
}
case agent.ToolExecutionStartEvent:
fmt.Fprintf(os.Stderr, "[tool] %s %s\n", event.Call.Name, event.Call.Arguments)
case agent.ToolExecutionEndEvent:
fmt.Fprintf(os.Stderr, "[done] %s error=%t\n", event.Call.Name, event.Result.IsError)
case agent.RunEndEvent:
if event.Err != nil {
fmt.Fprintln(os.Stderr, "run failed:", event.Err)
}
}
}

result, err := run.Wait()
if err != nil {
return err
}
fmt.Printf("final: %s\n", result.Final.Text())

In short, deltas are for display, agent.Result.Final is the completed response, and agent.Result.Context is the continuation state.

Following the event order

Events nest runs, turns, messages, and tool executions.

RunStartEvent
→ optional compaction start/end
→ TurnStartEvent
→ prompt MessageStartEvent / MessageEndEvent
→ assistant MessageStartEvent
→ MessageUpdateEvent* carrying generation content events
MessageEndEvent
→ tool executions and tool-result messages
TurnEndEvent
→ more turns and compactions
RunEndEvent

The * means zero or more updates per message. Message events cover only messages admitted or produced during this run, meaning new prompts, assistant attempts, and tool results. Messages already present in agent.Input.Context are request input and do not emit historical events.

RunStartEvent is always first and RunEndEvent always last. If cancellation happens before prompts are admitted and before compaction starts, the sequence can be RunStartEvent → RunEndEvent. If threshold compaction has started, its CompactionStartEvent and matching CompactionEndEvent occur before RunEndEvent.

Reading the nested generation events

agent.MessageUpdateEvent.AssistantMessageEvent carries one generation content event with its ContentIndex, delta, end value, and cumulative Partial. The rules in Observing generation events still apply. generation.StartEvent, generation.DoneEvent, and generation.ErrorEvent are represented by Agent message and run lifecycle events instead of being forwarded directly.

Knowing the twelve Agent events

EventMeaning
agent.RunStartEventrun began
agent.RunEndEventrun ended. Messages equals Result.NewMessages
agent.TurnStartEventa turn began, covering prompt admission, an assistant response, or a continued tool turn
agent.TurnEndEventresponse and ordered tool results ended
agent.MessageStartEventa prompt, assistant attempt, or tool result began
agent.MessageUpdateEventone generation content update
agent.MessageEndEventa message ended with its final value
agent.ToolExecutionStartEventa call was announced before lookup and validation
agent.ToolExecutionUpdateEventprovisional output from a running tool
agent.ToolExecutionEndEventfinal result message and termination flag for one call
agent.CompactionStartEventa summary rewrite began
agent.CompactionEndEventrewrite succeeded with a record or failed with an error

Three rules describe every tool event. ToolExecutionStartEvent appears even for unknown or malformed calls, and the matching end event carries an error result. Parallel start events follow assistant call order, while end events follow completion order. TurnEndEvent results follow assistant call order.

Consuming the stream carefully

run.Events() returns an iterator with one consumer. Calling Wait first selects result mode and leaves the event queue empty. Starting iteration claims the event stream, so call Wait after iteration ends. Breaking the iteration abandons the remaining queue but does not stop the run. Calling Wait repeatedly is safe.

Structural events and content start/end updates are retained. Incremental assistant deltas and tool updates may be dropped when the consumer falls behind, with at most 128 such events queued at once. Delivered values are copies, and errors keep their identity for errors.Is and errors.As.

Following events around cancellation and compaction

Run.Abort() cancels only its run, and a tool function that ignores cancellation can delay its end and the final RunEndEvent.

Threshold compaction is optional maintenance. Planning errors, estimate failures, and no-op plans emit no compaction pair. Once a rewrite starts, it emits CompactionStartEvent. A non-cancellation rewrite failure emits CompactionEndEvent.Err, leaves the conversation unchanged, and the run continues. After a successful answer, a non-cancellation failure preserves that answer and settles the run. Overflow recovery requires an attached Compactor, and a failed required rewrite ends the run. Successful rewrites appear in agent.Result.Compactions. The Compacting conversation context chapter covers both triggers.

API reference