Skip to main content
Use streaming when your application needs live output — chat UIs, progress indicators, or AG-UI-compatible frontends.
MethodReturnsBlocking
Run*AgentRunResultYes — waits for completion
RunAsync<-chan AgentRunAsyncResultNo — one result arrives on the channel when the run finishes
Stream<-chan AgentEventNo — events arrive as the run progresses
See Quickstart for a blocking Run example.

Enable streaming

Pass WithStream(true) at agent creation:
a, err := agent.NewAgent(
    agent.WithLLMClient(llmClient),
    agent.WithSystemPrompt("You are a helpful assistant."),
    agent.WithStream(true),
)
Then call Stream instead of Run:
eventCh, err := a.Stream(ctx, "What's 17 * 23?", nil)
if err != nil {
    return err
}

for ev := range eventCh {
    if ev == nil {
        continue
    }
    switch ev.Type() {
    case agent.AgentEventTypeTextMessageContent:
        if t, ok := ev.(*agent.AgentTextMessageContentEvent); ok {
            fmt.Print(t.Delta)
        }
    case agent.AgentEventTypeToolCallStart:
        if t, ok := ev.(*agent.AgentToolCallStartEvent); ok {
            fmt.Printf("\n[tool] %s\n", t.ToolCallName)
        }
    case agent.AgentEventTypeRunFinished:
        fmt.Println("\n[done]")
    case agent.AgentEventTypeRunError:
        if t, ok := ev.(*agent.AgentRunErrorEvent); ok {
            fmt.Printf("\n[error] %s\n", t.Message)
        }
    }
}
Always check if ev == nil { continue } before calling ev.Type(). The channel can deliver a nil sentinel to signal the end of a batch.

Event types

Stream events are typed AgentEvent values. Use ev.Type() and type-assert to the concrete struct to access fields.

Lifecycle

EventWhen
AgentEventTypeRunStartedRun begins
AgentEventTypeRunFinishedRun completes — carries final result on AgentRunFinishedEvent.Result
AgentEventTypeRunErrorRun failed — error message on AgentRunErrorEvent.Message
AgentEventTypeStepStartedSub-agent child workflow begins — StepName is the sub-agent route name
AgentEventTypeStepFinishedSub-agent child workflow ends

Text output

EventWhen
AgentEventTypeTextMessageStartAssistant message begins
AgentEventTypeTextMessageContentPartial text delta — print Delta as it arrives
AgentEventTypeTextMessageEndAssistant message ends

Tool calls

EventWhen
AgentEventTypeToolCallStartTool invocation begins — ToolCallName identifies the tool
AgentEventTypeToolCallArgsPartial tool arguments streaming in
AgentEventTypeToolCallEndTool invocation ends
AgentEventTypeToolCallResultTool result content

Reasoning

EventWhen
AgentEventTypeReasoningMessageContentPartial reasoning/thinking delta (Anthropic when extended thinking is enabled)

Custom (approvals and delegation)

EventWhen
AgentEventTypeCustomApproval or delegation requests during Stream
Parse approval events with ParseCustomEventApproval / ParseCustomEventDelegation, then call OnApproval with the ApprovalToken. See Approvals for the full flow.

Displaying streamed text

TEXT_MESSAGE_CONTENT deltas and AgentRunFinishedEvent.Result.Content carry the same text. Do not print both. If you rendered deltas live, skip printing result.Content from RUN_FINISHED.
When sub-agents are configured, events from delegated runs fan in to the parent stream. Use AgentName on typed events to identify which agent emitted them. Multiple RUN_FINISHED events may appear before the root run finishes — each corresponds to one completed sub-agent run.

Token usage from Stream

Aggregated token counts are on Result.LLMUsage inside AgentRunFinishedEvent:
for ev := range eventCh {
    if ev == nil {
        continue
    }
    if finished, ok := ev.(*agent.AgentRunFinishedEvent); ok && finished.Result != nil {
        if finished.Result.LLMUsage != nil {
            fmt.Printf("tokens: input=%d output=%d total=%d\n",
                finished.Result.LLMUsage.PromptTokens,
                finished.Result.LLMUsage.CompletionTokens,
                finished.Result.LLMUsage.TotalTokens,
            )
        }
    }
}
See Token Usage for field details.

RunAsync

RunAsync starts the run in a goroutine and returns a channel that delivers one AgentRunAsyncResult when the run completes. Use it when you want a final result without blocking the caller, but do not need live token events.
a, err := agent.NewAgent(
    agent.WithLLMClient(llmClient),
    agent.WithApprovalHandler(func(ctx context.Context, req *agent.ApprovalRequest) {
        _ = req.Respond(agent.ApprovalStatusApproved)
    }),
)

resultCh, err := a.RunAsync(ctx, "Summarize this document", nil)
if err != nil {
    return err
}

res := <-resultCh
if res.Error != nil {
    return res.Error
}
fmt.Println(res.Result.Content)
Handle tool approvals with WithApprovalHandler — the same callback as Run.

Streaming with conversation history

Pass ConversationOptions to share history across turns while streaming. The same session ID links messages across calls:
conv := inmem.NewInMemoryConversation(inmem.WithMaxSize(100))

a, err := agent.NewAgent(
    agent.WithLLMClient(llmClient),
    agent.WithStream(true),
    agent.WithConversation(conversation.Config{Conversation: conv, Size: 20}),
)

opts := &agent.AgentRunOptions{
    ConversationOptions: &agent.ConversationOptions{ID: "session-1"},
}

// First turn
eventCh, _ := a.Stream(ctx, "My name is Alice.", opts)
for ev := range eventCh { /* handle */ }

// Second turn — agent remembers the session
eventCh, _ = a.Stream(ctx, "What's my name?", opts)
for ev := range eventCh { /* handle */ }
See Conversation for backend options and lifecycle management.

AG-UI protocol

Stream events follow the AG-UI open protocol. Serialize any event with event.ToJSON() and forward over SSE or WebSocket to a compatible frontend. See AG-UI Protocol.

Temporal streaming notes

On the Temporal runtime, the agent run is durable but live stream delivery is not backfilled — see Temporal runtime.

Examples

Stream example

Partial tokens, tool events, and RUN_FINISHED handling

RunAsync example

Non-blocking run with approval handler

AG-UI example

Go SSE server with CopilotKit frontend

Approvals

Approval flow for Run, RunAsync, and Stream