@@ -987,6 +987,26 @@ def _normalize_empty_agent_response(
987987 return response
988988
989989
990+ def _should_clear_resume_pending_after_turn(agent_result: dict) -> bool:
991+ """Return True only when a gateway turn really completed successfully.
992+
993+ Restart recovery uses ``resume_pending`` as a durable marker for sessions
994+ interrupted during gateway drain. A soft interrupt can still bubble out as
995+ a syntactically normal agent result with an empty final response; clearing
996+ the marker in that case loses the recovery signal and startup auto-resume
997+ has nothing to schedule.
998+ """
999+ if not isinstance(agent_result, dict):
1000+ return False
1001+ if agent_result.get("interrupted"):
1002+ return False
1003+ if agent_result.get("failed") or agent_result.get("partial") or agent_result.get("error"):
1004+ return False
1005+ if agent_result.get("completed") is False:
1006+ return False
1007+ return True
1008+
1009+
9901010class GatewayRunner:
9911011 """
9921012 Main gateway controller.
@@ -6589,7 +6609,7 @@ async def _handle_message_with_agent(self, event, source, _quick_key: str, run_g
65896609 # shutdown) — the turn ran to completion, so recovery
65906610 # succeeded and subsequent messages should no longer receive
65916611 # the restart-interruption system note.
6592- if session_key:
6612+ if session_key and _should_clear_resume_pending_after_turn(agent_result) :
65936613 self._clear_restart_failure_count(session_key)
65946614 try:
65956615 self.session_store.clear_resume_pending(session_key)
@@ -14068,6 +14088,11 @@ def _approval_notify_sync(approval_data: dict) -> None:
1406814088 "messages": result.get("messages", []),
1406914089 "api_calls": result.get("api_calls", 0),
1407014090 "failed": result.get("failed", False),
14091+ "partial": result.get("partial", False),
14092+ "completed": result.get("completed"),
14093+ "interrupted": result.get("interrupted", False),
14094+ "interrupt_message": result.get("interrupt_message"),
14095+ "error": result.get("error"),
1407114096 "compression_exhausted": result.get("compression_exhausted", False),
1407214097 "tools": tools_holder[0] or [],
1407314098 "history_offset": len(agent_history),
@@ -14183,6 +14208,11 @@ def _approval_notify_sync(approval_data: dict) -> None:
1418314208 "last_reasoning": result.get("last_reasoning"),
1418414209 "messages": result_holder[0].get("messages", []) if result_holder[0] else [],
1418514210 "api_calls": result_holder[0].get("api_calls", 0) if result_holder[0] else 0,
14211+ "completed": result_holder[0].get("completed") if result_holder[0] else None,
14212+ "interrupted": result_holder[0].get("interrupted", False) if result_holder[0] else False,
14213+ "partial": result_holder[0].get("partial", False) if result_holder[0] else False,
14214+ "error": result_holder[0].get("error") if result_holder[0] else None,
14215+ "interrupt_message": result_holder[0].get("interrupt_message") if result_holder[0] else None,
1418614216 "tools": tools_holder[0] or [],
1418714217 "history_offset": _effective_history_offset,
1418814218 "last_prompt_tokens": _last_prompt_toks,
0 commit comments