Skip to content

Commit 76a695e

Browse files
authored
fix(agents-core): preserve nested agent-tool approval attribution on restore (#996)
1 parent 2de7fbf commit 76a695e

File tree

5 files changed

+43
-0
lines changed

5 files changed

+43
-0
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@openai/agents-core': patch
3+
---
4+
5+
fix: preserve nested agent tool approval agent after run state restore

packages/agents-core/src/agent.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ import {
4848
} from './agentToolInput';
4949
import type { ZodObjectLike } from './utils/zodCompat';
5050
import { saveAgentToolRunResult } from './agentToolRunResults';
51+
import { registerAgentToolSourceAgent } from './agentToolSourceRegistry';
5152

5253
type CompletedRunResult<TContext, TAgent extends Agent<TContext, any>> = (
5354
| RunResult<TContext, TAgent>
@@ -848,6 +849,7 @@ export class Agent<
848849
return agentTool;
849850
},
850851
};
852+
registerAgentToolSourceAgent(agentTool, this);
851853

852854
return agentTool;
853855
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import type { Agent } from './agent';
2+
import type { FunctionTool, Tool } from './tool';
3+
4+
// Internal registry that links Agent.asTool() function tools back to their source
5+
// Agent instances so RunState reconstruction can traverse nested agent-tools
6+
// without storing hidden metadata on the tool object itself.
7+
const agentToolSourceRegistry = new WeakMap<
8+
FunctionTool<any, any, any>,
9+
Agent<any, any>
10+
>();
11+
12+
export function registerAgentToolSourceAgent(
13+
tool: FunctionTool<any, any, any>,
14+
agent: Agent<any, any>,
15+
): void {
16+
agentToolSourceRegistry.set(tool, agent);
17+
}
18+
19+
export function getAgentToolSourceAgent(
20+
tool: Tool<any>,
21+
): Agent<any, any> | undefined {
22+
if (tool.type !== 'function') {
23+
return undefined;
24+
}
25+
return agentToolSourceRegistry.get(tool);
26+
}

packages/agents-core/src/runState.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { z } from 'zod';
22
import { Agent } from './agent';
3+
import { getAgentToolSourceAgent } from './agentToolSourceRegistry';
34
import {
45
RunMessageOutputItem,
56
RunItem,
@@ -997,6 +998,13 @@ export function buildAgentMap(
997998
}
998999
}
9991000
}
1001+
1002+
for (const tool of currentAgent.tools) {
1003+
const sourceAgent = getAgentToolSourceAgent(tool);
1004+
if (sourceAgent && !map.has(sourceAgent.name)) {
1005+
queue.push(sourceAgent);
1006+
}
1007+
}
10001008
}
10011009

10021010
return map;

packages/agents-core/test/agentScenarios.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,7 @@ describe('Agent scenarios (examples and docs patterns)', () => {
540540
const first = await runner.run(outerAgent, 'start');
541541

542542
expect(first.interruptions).toHaveLength(1);
543+
expect(first.interruptions?.[0]?.agent.name).toBe('NestedAgent');
543544

544545
const outerOutputsBefore = first.newItems.filter(
545546
(item) =>
@@ -557,6 +558,7 @@ describe('Agent scenarios (examples and docs patterns)', () => {
557558
if (!approval) {
558559
throw new Error('Expected nested tool approval interruption');
559560
}
561+
expect(approval.agent.name).toBe('NestedAgent');
560562
restored.approve(approval);
561563

562564
const resumed = await runner.run(outerAgent, restored);

0 commit comments

Comments
 (0)