-
Notifications
You must be signed in to change notification settings - Fork 3.2k
Closed
Labels
Milestone
Description
When using agent.as_tool() with needs_approval=True on a tool inside the nested agent, resuming after HITL approval starts a fresh inner run instead of continuing — causing an infinite pause/approve/resume cycle.
Root Cause
agent_tool_state.py stores nested run results in a module-level dict with weakref-based cleanup keyed by tool_call object identity.
When the caller:
- Serializes
RunStateviato_json() - Releases the
RunResult
The tool_call objects are garbage collected → weakref callbacks fire → cache entries are removed.
On resume:
peek_agent_tool_run_result()returnsNone- The SDK starts a fresh inner run
- It hits
needs_approvalagain - It pauses
- The cycle repeats
Repro
- Create an outer agent with an inner agent via
as_tool() - The inner agent has a tool with
needs_approval=True - Run → execution pauses for approval
- Serialize
result.to_state().to_json()and letresultgo out of scope - Deserialize with
RunState.from_json() - Call
Runner.run()with the resumed state + approved decisions - The inner agent starts fresh instead of resuming → pauses again on the same tool
Expected
RunState.to_json() should capture nested agent-as-tool run results so they survive serialization round-trips.
Workaround
Keep a strong reference to the RunResult object between pause and resume to prevent garbage collection of tool_call objects.
This only works within a single process lifetime.
Reactions are currently unavailable