-
Notifications
You must be signed in to change notification settings - Fork 497
Description
Please read this first
- Have you read the docs? Yes, I read the Agents SDK docs, with a focus on the streaming behavior and agent-as-tools / handoffs sections.
- Have you searched for related issues? Yes, I saw similar feature requests in the Python library around streaming nested tool/agent runs Agent.as_tool hides nested tool‑call events — blocks parallel sub‑agents with streaming openai-agents-python#864, but nothing specific for JS with recursive agent-as-tools + handoffs
Feature request
** Add a way to stream events from all agents involved in a run (main agent, agents used as tools, and agents reached via handoffs), through a single stream returned from Runner.run. This would be controlled by a new streamAgentTools?: boolean flag on StreamRunOptions when stream: true.
Concretely, when calling:
const runner = new Runner({ model: 'gpt-4.1-mini' });
const result = await runner.run(agentA, 'Solve task by using agentB as a tool', {
stream: true,
streamAgentTools: true,
});and agentA uses other agents as tools (e.g. agentB.asTool(), possibly nested and/or via handoffs), then:
- All
RunStreamEvents from agent A and any downstream agents (used as tools or reached via handoffs, recursively) are surfaced in the top-levelStreamedRunResult. - Each
raw_model_stream_eventis annotated with the originating agent name (e.g.event.agentName) so UIs can distinguish which agent produced which text or event. - Internally, the runner maintains a context-scoped stream attached to
RunContext:- When
streamAgentTools: trueis set for the top-level run, a “context scope stream” is created on the context and all subsequent streamed results in that context copy their events into it. - When an agent is invoked via
agent.asTool(...)within an active context-scope stream, that tool run automatically executes in streaming mode (stream: true) but withstreamAgentTools: falseto avoid recursive re-wiring; its events are still mirrored into the shared context-scope stream.
- When
Motivation
Today, when building an app where:
- A “main” agent orchestrates the conversation, and
- It uses multiple specialized agents as tools (and possibly handoffs to other agents),
the top-level stream only includes the main agent’s events. End users see that the main agent has called a tool (e.g. via tool_called / tool_output in run item events) but get no live updates from the tool agent itself. If the tool agent is slow or multi-step, this makes the UI feel stuck or unresponsive.
There are workarounds (e.g. writing custom updates in hooks, or wiring up separate streams per tool agent), but they require significant additional code and don’t compose nicely with nested agent-as-tools and handoffs. A simple, opt-in toggle like streamAgentTools: true makes it much easier to:
- Build rich, multi-agent UIs where users can see progress from all participating agents in real time.
- Support deep nesting (agent → agent-as-tool → handoff → further agent-as-tool, etc.) without writing custom streaming plumbing.
- Maintain a single, unified streaming contract (
StreamedRunResult) that works with existing API surface and event types (raw_model_stream_event,run_item_stream_event,agent_updated_stream_event).
Example usage
const weatherAgent = new Agent({ name: 'Weather Agent', tools: [getWeatherTool] });
const newsAgent = new Agent({
name: 'News Agent',
instructions: 'You are a news agent that can tell the news for a given city.',
tools: [getLocalNewsTool],
});
const personalAgent = new Agent({
name: 'Personal Agent',
instructions:
'You are a personal agent that prepares a user for the day. ' +
'You can use the news agent to get the news for the day, and the weather agent to get the weather for the day.',
tools: [
newsAgent.asTool({ toolName: 'news_agent', toolDescription: 'Get the local news for today' }),
weatherAgent.asTool({ toolName: 'weather_agent', toolDescription: 'Get the weather for today' }),
],
});
const runner = new Runner({
model: 'gpt-4.1-mini',
tracingDisabled: true,
});
const streamedRunResult = await runner.run(
personalAgent,
"What's up in Beijing today?",
{
stream: true,
// Enable recursive streaming of all agent-as-tool and handoff events
streamAgentTools: true,
},
);
// Events now include contributions from `Personal Agent`, `News Agent`, and `Weather Agent`.
for await (const event of streamedRunResult) {
// UI can render progress for all agents, using event.agentName where applicable.
}