@@ -492,22 +492,62 @@ async def __call__(self, result: str = "", iteration: int = 0) -> object:
492492
493493
494494class CheckTransferEntry :
495- """Detect transfer tool calls (was ``check_transfer_worker``; stateless)."""
495+ """Detect transfer tool calls (was ``check_transfer_worker``; stateless).
496+
497+ Selection is first-wins: when the LLM emits multiple transfer calls in one
498+ turn only the first is honored (the swarm loop can only hand off to one
499+ agent). The others are surfaced as ``dropped_transfers`` so the intent is
500+ visible in the task output instead of silently discarded — the transfer
501+ tool descriptions instruct the model to call at most one per turn.
502+
503+ ``transfer_message`` carries the transfer tool's ``message`` argument (the
504+ hand-off note for the receiving agent); the server compiler records it in
505+ the conversation as ``[agent -> target]: <message>``.
506+ """
496507
497508 async def __call__ (self , tool_calls : object = None , _unused : str = "" ) -> object :
509+ transfers = []
498510 for tc in tool_calls or []:
499- name = tc .get ("name" , "" )
500- if "_transfer_to_" in name :
501- return {"is_transfer" : True , "transfer_to" : name .split ("_transfer_to_" , 1 )[1 ]}
502- return {"is_transfer" : False , "transfer_to" : "" }
511+ name = tc .get ("name" , "" ) or ""
512+ if "_transfer_to_" not in name :
513+ continue
514+ params = tc .get ("inputParameters" ) or {}
515+ message = params .get ("message" )
516+ transfers .append (
517+ {
518+ "transfer_to" : name .split ("_transfer_to_" , 1 )[1 ],
519+ "message" : "" if message is None else str (message ),
520+ }
521+ )
522+ if not transfers :
523+ return {"is_transfer" : False , "transfer_to" : "" , "transfer_message" : "" }
524+ first = transfers [0 ]
525+ out = {
526+ "is_transfer" : True ,
527+ "transfer_to" : first ["transfer_to" ],
528+ "transfer_message" : first ["message" ],
529+ }
530+ if len (transfers ) > 1 :
531+ import logging
532+
533+ logging .getLogger (__name__ ).warning (
534+ "Multiple transfer calls in one turn; honoring '%s', dropping %s" ,
535+ first ["transfer_to" ],
536+ [t ["transfer_to" ] for t in transfers [1 :]],
537+ )
538+ out ["dropped_transfers" ] = transfers [1 :]
539+ return out
503540
504541
505542class TransferNoopEntry :
506543 """No-op transfer tool (was the nested ``transfer_worker``; handoff is
507- detected by check_transfer from toolCalls output)."""
544+ detected by check_transfer from toolCalls output). Echoes the hand-off
545+ ``message`` so it is visible in the task output / UI."""
508546
509- async def __call__ (self ) -> object :
510- return {}
547+ async def __call__ (self , message : str = "" ) -> object :
548+ if not message :
549+ return {}
550+ return {"message" : message }
511551
512552
513553class TransferUnreachableEntry :
0 commit comments