@@ -814,49 +814,66 @@ async def _run_loop(
814814 Yields:
815815 Events from the event loop cycle.
816816 """
817- before_invocation_event , _interrupts = await self .hooks .invoke_callbacks_async (
818- BeforeInvocationEvent (agent = self , invocation_state = invocation_state , messages = messages )
819- )
820- messages = before_invocation_event .messages if before_invocation_event .messages is not None else messages
817+ current_messages : Messages | None = messages
821818
822- agent_result : AgentResult | None = None
823- try :
824- yield InitEventLoopEvent ()
819+ while current_messages is not None :
820+ before_invocation_event , _interrupts = await self .hooks .invoke_callbacks_async (
821+ BeforeInvocationEvent (agent = self , invocation_state = invocation_state , messages = current_messages )
822+ )
823+ current_messages = (
824+ before_invocation_event .messages if before_invocation_event .messages is not None else current_messages
825+ )
825826
826- await self ._append_messages (* messages )
827+ agent_result : AgentResult | None = None
828+ try :
829+ yield InitEventLoopEvent ()
827830
828- structured_output_context = StructuredOutputContext (
829- structured_output_model or self ._default_structured_output_model ,
830- structured_output_prompt = structured_output_prompt or self ._structured_output_prompt ,
831- )
831+ await self ._append_messages (* current_messages )
832832
833- # Execute the event loop cycle with retry logic for context limits
834- events = self ._execute_event_loop_cycle (invocation_state , structured_output_context )
835- async for event in events :
836- # Signal from the model provider that the message sent by the user should be redacted,
837- # likely due to a guardrail.
838- if (
839- isinstance (event , ModelStreamChunkEvent )
840- and event .chunk
841- and event .chunk .get ("redactContent" )
842- and event .chunk ["redactContent" ].get ("redactUserContentMessage" )
843- ):
844- self .messages [- 1 ]["content" ] = self ._redact_user_content (
845- self .messages [- 1 ]["content" ], str (event .chunk ["redactContent" ]["redactUserContentMessage" ])
846- )
847- if self ._session_manager :
848- self ._session_manager .redact_latest_message (self .messages [- 1 ], self )
849- yield event
833+ structured_output_context = StructuredOutputContext (
834+ structured_output_model or self ._default_structured_output_model ,
835+ structured_output_prompt = structured_output_prompt or self ._structured_output_prompt ,
836+ )
850837
851- # Capture the result from the final event if available
852- if isinstance (event , EventLoopStopEvent ):
853- agent_result = AgentResult (* event ["stop" ])
838+ # Execute the event loop cycle with retry logic for context limits
839+ events = self ._execute_event_loop_cycle (invocation_state , structured_output_context )
840+ async for event in events :
841+ # Signal from the model provider that the message sent by the user should be redacted,
842+ # likely due to a guardrail.
843+ if (
844+ isinstance (event , ModelStreamChunkEvent )
845+ and event .chunk
846+ and event .chunk .get ("redactContent" )
847+ and event .chunk ["redactContent" ].get ("redactUserContentMessage" )
848+ ):
849+ self .messages [- 1 ]["content" ] = self ._redact_user_content (
850+ self .messages [- 1 ]["content" ],
851+ str (event .chunk ["redactContent" ]["redactUserContentMessage" ]),
852+ )
853+ if self ._session_manager :
854+ self ._session_manager .redact_latest_message (self .messages [- 1 ], self )
855+ yield event
856+
857+ # Capture the result from the final event if available
858+ if isinstance (event , EventLoopStopEvent ):
859+ agent_result = AgentResult (* event ["stop" ])
854860
855- finally :
856- self .conversation_manager .apply_management (self )
857- await self .hooks .invoke_callbacks_async (
858- AfterInvocationEvent (agent = self , invocation_state = invocation_state , result = agent_result )
859- )
861+ finally :
862+ self .conversation_manager .apply_management (self )
863+ after_invocation_event , _interrupts = await self .hooks .invoke_callbacks_async (
864+ AfterInvocationEvent (agent = self , invocation_state = invocation_state , result = agent_result )
865+ )
866+
867+ # Convert resume input to messages for next iteration, or None to stop
868+ if after_invocation_event .resume is not None :
869+ logger .debug ("resume=<True> | hook requested agent resume with new input" )
870+ # If in interrupt state, process interrupt responses before continuing.
871+ # This mirrors the _interrupt_state.resume() call in stream_async and will
872+ # raise TypeError if the resume input is not valid interrupt responses.
873+ self ._interrupt_state .resume (after_invocation_event .resume )
874+ current_messages = await self ._convert_prompt_to_messages (after_invocation_event .resume )
875+ else :
876+ current_messages = None
860877
861878 async def _execute_event_loop_cycle (
862879 self , invocation_state : dict [str , Any ], structured_output_context : StructuredOutputContext | None = None
0 commit comments