# Results When you call the `Runner.run` methods, you receive one of two result types: - [`RunResult`][agents.result.RunResult] from `Runner.run(...)` or `Runner.run_sync(...)` - [`RunResultStreaming`][agents.result.RunResultStreaming] from `Runner.run_streamed(...)` Both inherit from [`RunResultBase`][agents.result.RunResultBase], which exposes the shared result surfaces such as `final_output`, `new_items`, `last_agent`, `raw_responses`, and `to_state()`. `RunResultStreaming` adds streaming-specific controls such as [`stream_events()`][agents.result.RunResultStreaming.stream_events], [`current_agent`][agents.result.RunResultStreaming.current_agent], [`is_complete`][agents.result.RunResultStreaming.is_complete], and [`cancel(...)`][agents.result.RunResultStreaming.cancel]. ## Choose the right result surface Most applications only need a few result properties or helpers: | If you need... | Use | | --- | --- | | The final answer to show the user | `final_output` | | A replay-ready next-turn input list with the full local transcript | `to_input_list()` | | Rich run items with agent, tool, handoff, and approval metadata | `new_items` | | The agent that should usually handle the next user turn | `last_agent` | | OpenAI Responses API chaining with `previous_response_id` | `last_response_id` | | Pending approvals and a resumable snapshot | `interruptions` and `to_state()` | | Metadata about the current nested `Agent.as_tool()` invocation | `agent_tool_invocation` | | Raw model calls or guardrail diagnostics | `raw_responses` and the guardrail result arrays | ## Final output The [`final_output`][agents.result.RunResultBase.final_output] property contains the final output of the last agent that ran. This is either: - a `str`, if the last agent did not have an `output_type` defined - an object of type `last_agent.output_type`, if the last agent had an output type defined - `None`, if the run stopped before a final output was produced, for example because it paused on an approval interruption !!! note `final_output` is typed as `Any`. Handoffs can change which agent finishes the run, so the SDK cannot statically know the full set of possible output types. In streaming mode, `final_output` stays `None` until the stream has finished processing. See [Streaming](streaming.md) for the event-by-event flow. ## Input, next-turn history, and new items These surfaces answer different questions: | Property or helper | What it contains | Best for | | --- | --- | --- | | [`input`][agents.result.RunResultBase.input] | The base input for this run segment. If a handoff input filter rewrote the history, this reflects the filtered input the run continued with. | Auditing what this run actually used as input | | [`to_input_list()`][agents.result.RunResultBase.to_input_list] | An input-item view of the run. The default `mode="preserve_all"` keeps the full converted history from `new_items`; `mode="normalized"` prefers canonical continuation input when handoff filtering rewrites model history. | Manual chat loops, client-managed conversation state, and plain-item history inspection | | [`new_items`][agents.result.RunResultBase.new_items] | Rich [`RunItem`][agents.items.RunItem] wrappers with agent, tool, handoff, and approval metadata. | Logs, UIs, audits, and debugging | | [`raw_responses`][agents.result.RunResultBase.raw_responses] | Raw [`ModelResponse`][agents.items.ModelResponse] objects from each model call in the run. | Provider-level diagnostics or raw response inspection | In practice: - Use `to_input_list()` when you want a plain input-item view of the run. - Use `to_input_list(mode="normalized")` when you want the canonical local input for the next `Runner.run(..., input=...)` call after handoff filtering or nested handoff history rewrites. - Use [`session=...`](sessions/index.md) when you want the SDK to load and save history for you. - If you are using OpenAI server-managed state with `conversation_id` or `previous_response_id`, usually pass only the new user input and reuse the stored ID instead of resending `to_input_list()`. - Use the default `to_input_list()` mode or `new_items` when you need the full converted history for logs, UIs, or audits. Unlike the JavaScript SDK, Python does not expose a separate `output` property for the model-shaped delta only. Use `new_items` when you need SDK metadata, or inspect `raw_responses` when you need the raw model payloads. Computer-tool replay follows the raw Responses payload shape. Preview-model `computer_call` items preserve a single `action`, while `gpt-5.5` computer calls can preserve batched `actions[]`. [`to_input_list()`][agents.result.RunResultBase.to_input_list] and [`RunState`][agents.run_state.RunState] keep whichever shape the model produced, so manual replay, pause/resume flows, and stored transcripts continue to work across both preview and GA computer-tool calls. Local execution results still appear as `computer_call_output` items in `new_items`. ### New items [`new_items`][agents.result.RunResultBase.new_items] gives you the richest view of what happened during the run. Common item types are: - [`MessageOutputItem`][agents.items.MessageOutputItem] for assistant messages - [`ReasoningItem`][agents.items.ReasoningItem] for reasoning items - [`ToolSearchCallItem`][agents.items.ToolSearchCallItem] and [`ToolSearchOutputItem`][agents.items.ToolSearchOutputItem] for Responses tool search requests and loaded tool-search results - [`ToolCallItem`][agents.items.ToolCallItem] and [`ToolCallOutputItem`][agents.items.ToolCallOutputItem] for tool calls and their results - [`ToolApprovalItem`][agents.items.ToolApprovalItem] for tool calls that paused for approval - [`HandoffCallItem`][agents.items.HandoffCallItem] and [`HandoffOutputItem`][agents.items.HandoffOutputItem] for handoff requests and completed transfers Choose `new_items` over `to_input_list()` whenever you need agent associations, tool outputs, handoff boundaries, or approval boundaries. When you use hosted tool search, inspect `ToolSearchCallItem.raw_item` to see the search request the model emitted, and `ToolSearchOutputItem.raw_item` to see which namespaces, functions, or hosted MCP servers were loaded for that turn. ## Continue or resume the conversation ### Next-turn agent [`last_agent`][agents.result.RunResultBase.last_agent] contains the last agent that ran. This is often the best agent to reuse for the next user turn after handoffs. In streaming mode, [`RunResultStreaming.current_agent`][agents.result.RunResultStreaming.current_agent] updates as the run progresses, so you can observe handoffs before the stream finishes. ### Interruptions and run state If a tool needs approval, pending approvals are exposed in [`RunResult.interruptions`][agents.result.RunResult.interruptions] or [`RunResultStreaming.interruptions`][agents.result.RunResultStreaming.interruptions]. This can include approvals raised by direct tools, by tools reached after a handoff, or by nested [`Agent.as_tool()`][agents.agent.Agent.as_tool] runs. Call [`to_state()`][agents.result.RunResult.to_state] to capture a resumable [`RunState`][agents.run_state.RunState], approve or reject the pending items, and then resume with `Runner.run(...)` or `Runner.run_streamed(...)`. ```python from agents import Agent, Runner agent = Agent(name="Assistant", instructions="Use tools when needed.") result = await Runner.run(agent, "Delete temp files that are no longer needed.") if result.interruptions: state = result.to_state() for interruption in result.interruptions: state.approve(interruption) result = await Runner.run(agent, state) ``` For streaming runs, finish consuming [`stream_events()`][agents.result.RunResultStreaming.stream_events] first, then inspect `result.interruptions` and resume from `result.to_state()`. For the full approval flow, see [Human-in-the-loop](human_in_the_loop.md). ### Server-managed continuation [`last_response_id`][agents.result.RunResultBase.last_response_id] is the latest model response ID from the run. Pass it back as `previous_response_id` on the next turn when you want to continue an OpenAI Responses API chain. If you already continue the conversation with `to_input_list()`, `session`, or `conversation_id`, you usually do not need `last_response_id`. If you need every model response from a multi-step run, inspect `raw_responses` instead. ## Agent-as-tool metadata When a result comes from a nested [`Agent.as_tool()`][agents.agent.Agent.as_tool] run, [`agent_tool_invocation`][agents.result.RunResultBase.agent_tool_invocation] exposes immutable metadata about the outer tool call: - `tool_name` - `tool_call_id` - `tool_arguments` For ordinary top-level runs, `agent_tool_invocation` is `None`. This is especially useful inside `custom_output_extractor`, where you may need the outer tool name, call ID, or raw arguments while post-processing the nested result. See [Tools](tools.md) for the surrounding `Agent.as_tool()` patterns. If you also need the parsed structured input for that nested run, read `context_wrapper.tool_input`. That is the field [`RunState`][agents.run_state.RunState] serializes generically for nested tool input, while `agent_tool_invocation` is the live result accessor for the current nested invocation. ## Streaming lifecycle and diagnostics [`RunResultStreaming`][agents.result.RunResultStreaming] inherits the same result surfaces above, but adds streaming-specific controls: - [`stream_events()`][agents.result.RunResultStreaming.stream_events] to consume semantic stream events - [`current_agent`][agents.result.RunResultStreaming.current_agent] to track the active agent mid-run - [`is_complete`][agents.result.RunResultStreaming.is_complete] to see whether the streamed run has fully finished - [`cancel(...)`][agents.result.RunResultStreaming.cancel] to stop the run immediately or after the current turn Keep consuming `stream_events()` until the async iterator finishes. A streaming run is not complete until that iterator ends, and summary properties such as `final_output`, `interruptions`, `raw_responses`, and session-persistence side effects may still be settling after the last visible token arrives. If you call `cancel()`, continue consuming `stream_events()` so cancellation and cleanup can finish correctly. Python does not expose a separate streamed `completed` promise or `error` property. Terminal streaming failures are surfaced by raising from `stream_events()`, and `is_complete` reflects whether the run has reached its terminal state. ### Raw responses [`raw_responses`][agents.result.RunResultBase.raw_responses] contains the raw model responses collected during the run. Multi-step runs can produce more than one response, for example across handoffs or repeated model/tool/model cycles. [`last_response_id`][agents.result.RunResultBase.last_response_id] is just the ID from the last entry in `raw_responses`. ### Guardrail results Agent-level guardrails are exposed as [`input_guardrail_results`][agents.result.RunResultBase.input_guardrail_results] and [`output_guardrail_results`][agents.result.RunResultBase.output_guardrail_results]. Tool guardrails are exposed separately as [`tool_input_guardrail_results`][agents.result.RunResultBase.tool_input_guardrail_results] and [`tool_output_guardrail_results`][agents.result.RunResultBase.tool_output_guardrail_results]. These arrays accumulate across the run, so they are useful for logging decisions, storing extra guardrail metadata, or debugging why a run was blocked. ### Context and usage [`context_wrapper`][agents.result.RunResultBase.context_wrapper] exposes your app context together with SDK-managed runtime metadata such as approvals, usage, and nested `tool_input`. Usage is tracked on `context_wrapper.usage`. For streamed runs, the usage totals can lag until the stream's final chunks have been processed. See [Context management](context.md) for the full wrapper shape and persistence caveats.