Repository: openai/openai-agents-js (2025-07-13T18:31:28Z)

Issue:
agent_updated_stream_event passes previous agent, not the target/current agent
I’ve noticed that the agent_updated_stream_event payload returns the previous agent, not the new one (stream.currentAgent). For example, if the call starts with Agent A and then hands off to Agent B, the event payload looks like this:
```ts
{ 
  type: 'agent_updated_stream_event', 
  agent: { 
    name: 'Agent A' 
  } 
}
```

### Debug information

- Agents SDK version: (e.g. `v0.0.11`)
- Runtime environment (e.g. `Node.js 22.16.0`)

### Repro steps

Run this minimal TypeScript snippet to reproduce the issue:
```ts
import { Agent, Runner } from '@openai/agents'
import chalk from 'chalk'

const agentB = new Agent({
    name: 'Agent B',
})

const agentA = new Agent({
    name: 'Agent A',
    handoffs: [agentB],
    instructions: `You are a helpful assistant. You will hand off to Agent B when asked math questions.`,
})

const runner = new Runner({
    model: 'gpt-4.1-mini',
})

async function main() {
    const stream = await runner.run(agentA, 'What is the answer to: 2 + 3', { stream: true })

    for await (const event of stream) {
        // these are the raw events from the model
        if (event.type === 'raw_model_stream_event') {
            console.log(`${chalk.bgWhite(event.type)} %o`, event.data)
        }

        // agent updated events
        if (event.type == 'agent_updated_stream_event') {
            console.log(`${chalk.bgGreen(event.type)} New agent: %s`, event.agent.name)
        }

        // Agent SDK specific events
        if (event.type === 'run_item_stream_event') {
            console.log(`${chalk.bgYellow(event.type)} %o`, event.item)
        }
    }
}

main().catch(console.error)
```

Observe the agent_updated_stream_event payload.

Actual vs. Expected
- Actual: The agent field contains the old agent’s name.
- Expected: The agent field should contain the new agent’s name (the target of the handoff).

### Expected behaviour

This does't seem like intuitive behaviour and I'm not sure if it was intended. I would've expected to see the target agent in the payload, not the agent I already knew was active. In other words, I'd expect this event to tell me what agent we're handing off to, not which agent we've handed off from. 


Also, please consider adding a Streaming Events section to the documentation that:
- [x] Lists each event type (e.g. agent_updated_stream_event)
- [ ] Describes its payload schema
- [ ] Gives example payloads for common scenarios


Discussion:
hey @MarkMurphy, thanks for surfacing this and for the detailed repro. Can confirm from your sample run that right after the handoff we see (edited for brevity): 

```
run_item_stream_event RunHandoffOutputItem {
  sourceAgent: Agent { name: 'Agent A' },  
  targetAgent: Agent { name: 'Agent B' }  
}
agent_updated_stream_event New agent: Agent A   (should be “Agent B”)

```
So the `agent_updated_stream_event` payload is indeed carrying the previous agent instead of the new one.  Will make a PR to fix the JS emitter so `RunAgentUpdatedStreamEvent` actually gets the new agent.

On the docs side, we could perhaps mirror the Python SDK’s `Streaming Events` reference by adding a “Streaming Events” section to the existing JS streaming guide, listing each event type with its payload schema and JSON examples (handoffs included).



Patch:
diff --git a/.changeset/twelve-poets-mate.md b/.changeset/twelve-poets-mate.md
new file mode 100644
index 00000000..3551e1e6
--- /dev/null
+++ b/.changeset/twelve-poets-mate.md
@@ -0,0 +1,5 @@
+---
+'@openai/agents-core': patch
+---
+
+Fixes handling of `agent_updated_stream_event` in run implementation and adds corresponding test coverage.
diff --git a/packages/agents-core/src/run.ts b/packages/agents-core/src/run.ts
index 3169e19d..a0c4c4a4 100644
--- a/packages/agents-core/src/run.ts
+++ b/packages/agents-core/src/run.ts
@@ -824,7 +824,9 @@ export class Runner extends RunHooks<any, AgentOutputType<unknown>> {
             resetCurrentSpan();
           }
           result.state._currentAgentSpan = undefined;
-          result._addItem(new RunAgentUpdatedStreamEvent(currentAgent));
+          result._addItem(
+            new RunAgentUpdatedStreamEvent(result.state._currentAgent),
+          );
           result.state._noActiveAgentRun = true;
 
           // we've processed the handoff, so we need to run the loop again


Test patch:
diff --git a/packages/agents-core/test/run.stream.test.ts b/packages/agents-core/test/run.stream.test.ts
index 2e0d7f1d..f7b29f86 100644
--- a/packages/agents-core/test/run.stream.test.ts
+++ b/packages/agents-core/test/run.stream.test.ts
@@ -4,8 +4,17 @@ import {
   run,
   setDefaultModelProvider,
   setTracingDisabled,
+  Usage,
+  RunStreamEvent,
+  RunAgentUpdatedStreamEvent,
+  handoff,
+  Model,
+  ModelRequest,
+  ModelResponse,
+  StreamEvent,
+  FunctionCallItem,
 } from '../src';
-import { FakeModel, FakeModelProvider } from './stubs';
+import { FakeModel, FakeModelProvider, fakeModelMessage } from './stubs';
 
 // Test for unhandled rejection when stream loop throws
 
@@ -43,4 +52,67 @@ describe('Runner.run (streaming)', () => {
 
     expect((result.error as Error).message).toBe('Not implemented');
   });
+
+  it('emits agent_updated_stream_event with new agent on handoff', async () => {
+    class SimpleStreamingModel implements Model {
+      constructor(private resp: ModelResponse) {}
+      async getResponse(_req: ModelRequest): Promise<ModelResponse> {
+        return this.resp;
+      }
+      async *getStreamedResponse(): AsyncIterable<StreamEvent> {
+        yield {
+          type: 'response_done',
+          response: {
+            id: 'r',
+            usage: {
+              requests: 1,
+              inputTokens: 0,
+              outputTokens: 0,
+              totalTokens: 0,
+            },
+            output: this.resp.output,
+          },
+        } as any;
+      }
+    }
+
+    const agentB = new Agent({
+      name: 'B',
+      model: new SimpleStreamingModel({
+        output: [fakeModelMessage('done B')],
+        usage: new Usage(),
+      }),
+    });
+
+    const callItem: FunctionCallItem = {
+      id: 'h1',
+      type: 'function_call',
+      name: handoff(agentB).toolName,
+      callId: 'c1',
+      status: 'completed',
+      arguments: '{}',
+    };
+
+    const agentA = new Agent({
+      name: 'A',
+      model: new SimpleStreamingModel({
+        output: [callItem],
+        usage: new Usage(),
+      }),
+      handoffs: [handoff(agentB)],
+    });
+
+    const result = await run(agentA, 'hi', { stream: true });
+    const events: RunStreamEvent[] = [];
+    for await (const e of result.toStream()) {
+      events.push(e);
+    }
+    await result.completed;
+
+    const update = events.find(
+      (e): e is RunAgentUpdatedStreamEvent =>
+        e.type === 'agent_updated_stream_event',
+    );
+    expect(update?.agent).toBe(agentB);
+  });
 });