@@ -1898,11 +1898,63 @@ def _build_agent_map(initial_agent: Agent[Any]) -> dict[str, Agent[Any]]:
18981898 agent_map [current .name ] = current
18991899
19001900 # Add handoff agents to the queue
1901- for handoff in current .handoffs :
1902- # Handoff can be either an Agent or a Handoff object with an .agent attribute
1903- handoff_agent = handoff if not hasattr (handoff , "agent" ) else handoff .agent
1904- if handoff_agent and handoff_agent .name not in agent_map : # type: ignore[union-attr]
1905- queue .append (handoff_agent ) # type: ignore[arg-type]
1901+ for handoff_item in current .handoffs :
1902+ handoff_agent : Any | None = None
1903+ handoff_agent_name : str | None = None
1904+
1905+ if isinstance (handoff_item , Handoff ):
1906+ # Some custom/mocked Handoff subclasses bypass dataclass initialization.
1907+ # Prefer agent_name, then legacy name fallback used in tests.
1908+ candidate_name = getattr (handoff_item , "agent_name" , None ) or getattr (
1909+ handoff_item , "name" , None
1910+ )
1911+ if isinstance (candidate_name , str ):
1912+ handoff_agent_name = candidate_name
1913+ if handoff_agent_name in agent_map :
1914+ continue
1915+
1916+ handoff_ref = getattr (handoff_item , "_agent_ref" , None )
1917+ handoff_agent = handoff_ref () if callable (handoff_ref ) else None
1918+ if handoff_agent is None :
1919+ # Backward-compatibility fallback for custom legacy handoff objects that store
1920+ # the target directly on `.agent`. New code should prefer `handoff()` objects.
1921+ legacy_agent = getattr (handoff_item , "agent" , None )
1922+ if legacy_agent is not None :
1923+ handoff_agent = legacy_agent
1924+ logger .debug (
1925+ "Using legacy handoff `.agent` fallback while building agent map. "
1926+ "This compatibility path is not recommended for new code."
1927+ )
1928+ if handoff_agent_name is None :
1929+ candidate_name = getattr (handoff_agent , "name" , None )
1930+ handoff_agent_name = candidate_name if isinstance (candidate_name , str ) else None
1931+ if handoff_agent is None or not hasattr (handoff_agent , "handoffs" ):
1932+ if handoff_agent_name :
1933+ logger .debug (
1934+ "Skipping unresolved handoff target while building agent map: %s" ,
1935+ handoff_agent_name ,
1936+ )
1937+ continue
1938+ else :
1939+ # Backward-compatibility fallback for custom legacy handoff wrappers that expose
1940+ # the target directly on `.agent` without inheriting from `Handoff`.
1941+ legacy_agent = getattr (handoff_item , "agent" , None )
1942+ if legacy_agent is not None :
1943+ handoff_agent = legacy_agent
1944+ logger .debug (
1945+ "Using legacy non-`Handoff` `.agent` fallback while building agent map."
1946+ )
1947+ else :
1948+ handoff_agent = handoff_item
1949+ candidate_name = getattr (handoff_agent , "name" , None )
1950+ handoff_agent_name = candidate_name if isinstance (candidate_name , str ) else None
1951+
1952+ if (
1953+ handoff_agent is not None
1954+ and handoff_agent_name
1955+ and handoff_agent_name not in agent_map
1956+ ):
1957+ queue .append (cast (Any , handoff_agent ))
19061958
19071959 # Include agent-as-tool instances so nested approvals can be restored.
19081960 tools = getattr (current , "tools" , None )
0 commit comments