Skip to content

feat: automatic back-handoff to orchestrating agents#3584

Open
Oxygen56 wants to merge 1 commit into
openai:mainfrom
Oxygen56:feat/auto-back-handoff-847
Open

feat: automatic back-handoff to orchestrating agents#3584
Oxygen56 wants to merge 1 commit into
openai:mainfrom
Oxygen56:feat/auto-back-handoff-847

Conversation

@Oxygen56
Copy link
Copy Markdown

@Oxygen56 Oxygen56 commented Jun 5, 2026

Implements automatic back-handoff support as requested in #847.

When an agent is reached via a handoff, it can now automatically hand back to the originating agent upon completion by setting auto_handoff_back=True on the handoff configuration.

Problem

Currently, handoffs are one-way. Once an agent hands off control to another agent, it will never regain control without explicit circular handoff configurations. This makes orchestration patterns (where a main agent delegates to specialists and expects results back) tedious to implement.

Solution

This PR adds an auto_handoff_back: bool = False parameter to the Handoff class and handoff() factory function. When enabled:

  • The handoff chain is tracked in the run loop
  • When the child agent produces a final output, control automatically returns to the originating agent
  • The child's output is appended as a message in the conversation so the parent can use it

Changes

  • src/agents/handoffs/__init__.py: Add auto_handoff_back field to Handoff class and handoff() factory
  • src/agents/run_internal/run_steps.py: Extend NextStepHandoff to carry auto-handoff-back metadata
  • src/agents/run_internal/turn_resolution.py: Pass auto-handoff-back info through execute_handoffs()
  • src/agents/run.py: Track handoff chain and handle auto back-handoff in non-streaming path
  • src/agents/run_internal/run_loop.py: Track handoff chain and handle auto back-handoff in streaming path
  • tests/test_auto_handoff_back.py: Comprehensive tests for auto back-handoff behavior

Usage

from agents import Agent, Runner, handoff

specialist = Agent(name="specialist", instructions="...")
orchestrator = Agent(
    name="orchestrator",
    instructions="...",
    handoffs=[
        handoff(specialist, auto_handoff_back=True)
    ]
)

result = await Runner.run(orchestrator, "help me with this")
# After specialist finishes, control returns to orchestrator
assert result.last_agent.name == "orchestrator"

Closes #847

When an agent is reached via a handoff, it can now automatically
hand back to the originating agent upon completion. Controlled by
the auto_handoff_back parameter on the Handoff/handoff() config.

This enables orchestration patterns where a main agent delegates to
specialist agents and expects results back without needing explicit
circular handoff configurations.

Changes:
- Add auto_handoff_back: bool = False to Handoff class
- Pass through auto_handoff_back in handoff() factory function
- Extend NextStepHandoff to carry auto_handoff_back and originating_agent
- Track handoff chain in both streaming and non-streaming run loops
- Auto-handoff back when child agent produces final output
- Add comprehensive tests for the feature

Closes openai#847

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 792258f496

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

input_type: type[THandoffInput] | None = None,
input_filter: Callable[[HandoffInputData], HandoffInputData] | None = None,
nest_handoff_history: bool | None = None,
auto_handoff_back: bool = False,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve positional is_enabled binding

This inserts auto_handoff_back before the existing is_enabled positional slot in the runtime implementation, so existing callers that passed is_enabled positionally now bind that value to auto_handoff_back and leave is_enabled at its default True. For example, a previously disabled handoff using positional arguments is silently re-enabled, which breaks the public API positional-compatibility contract; the new optional parameter needs to be appended after is_enabled or handled with a compatibility shim.

Useful? React with 👍 / 👎.

Comment on lines +1152 to +1153
turn_session_items.append(back_msg)
streamed_result.new_items.append(back_msg)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Feed the back-handoff result to streamed parent turns

In the streaming path, this synthetic child-result message is appended only to new_items/session items, but the next streamed turn is built from streamed_result._model_input_items (set earlier to pre_step_items + new_step_items). When Runner.run_streamed() auto-hands back, the parent agent is invoked without the specialist's final output in its model input, so the advertised orchestration pattern fails for streaming runs.

Useful? React with 👍 / 👎.

Comment thread src/agents/run.py
Comment on lines +1028 to +1029
if next_step.auto_handoff_back and next_step.originating_agent is not None:
handoff_chain.append(next_step.originating_agent)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Persist the auto-handoff stack across resume

This stores the originating agent only in a local handoff_chain, but interruption results persist and resume from RunState, which does not include that local stack. If the child agent pauses for approval or another interruption after an auto handoff, resuming the run loses the parent agent, and the child's eventual final output is returned as the run result instead of handing control back.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support automatic "back" handoffs to orchestrating Agents

1 participant