@@ -985,6 +985,26 @@ def _normalize_empty_agent_response(
985985 return response
986986
987987
988+ def _should_clear_resume_pending_after_turn(agent_result: dict) -> bool:
989+ """Return True only when a gateway turn really completed successfully.
990+
991+ Restart recovery uses ``resume_pending`` as a durable marker for sessions
992+ interrupted during gateway drain. A soft interrupt can still bubble out as
993+ a syntactically normal agent result with an empty final response; clearing
994+ the marker in that case loses the recovery signal and startup auto-resume
995+ has nothing to schedule.
996+ """
997+ if not isinstance(agent_result, dict):
998+ return False
999+ if agent_result.get("interrupted"):
1000+ return False
1001+ if agent_result.get("failed") or agent_result.get("partial") or agent_result.get("error"):
1002+ return False
1003+ if agent_result.get("completed") is False:
1004+ return False
1005+ return True
1006+
1007+
9881008class GatewayRunner:
9891009 """
9901010 Main gateway controller.
@@ -6587,7 +6607,7 @@ async def _handle_message_with_agent(self, event, source, _quick_key: str, run_g
65876607 # shutdown) — the turn ran to completion, so recovery
65886608 # succeeded and subsequent messages should no longer receive
65896609 # the restart-interruption system note.
6590- if session_key:
6610+ if session_key and _should_clear_resume_pending_after_turn(agent_result) :
65916611 self._clear_restart_failure_count(session_key)
65926612 try:
65936613 self.session_store.clear_resume_pending(session_key)
@@ -13963,6 +13983,11 @@ def _approval_notify_sync(approval_data: dict) -> None:
1396313983 "messages": result.get("messages", []),
1396413984 "api_calls": result.get("api_calls", 0),
1396513985 "failed": result.get("failed", False),
13986+ "partial": result.get("partial", False),
13987+ "completed": result.get("completed"),
13988+ "interrupted": result.get("interrupted", False),
13989+ "interrupt_message": result.get("interrupt_message"),
13990+ "error": result.get("error"),
1396613991 "compression_exhausted": result.get("compression_exhausted", False),
1396713992 "tools": tools_holder[0] or [],
1396813993 "history_offset": len(agent_history),
@@ -14078,6 +14103,11 @@ def _approval_notify_sync(approval_data: dict) -> None:
1407814103 "last_reasoning": result.get("last_reasoning"),
1407914104 "messages": result_holder[0].get("messages", []) if result_holder[0] else [],
1408014105 "api_calls": result_holder[0].get("api_calls", 0) if result_holder[0] else 0,
14106+ "completed": result_holder[0].get("completed") if result_holder[0] else None,
14107+ "interrupted": result_holder[0].get("interrupted", False) if result_holder[0] else False,
14108+ "partial": result_holder[0].get("partial", False) if result_holder[0] else False,
14109+ "error": result_holder[0].get("error") if result_holder[0] else None,
14110+ "interrupt_message": result_holder[0].get("interrupt_message") if result_holder[0] else None,
1408114111 "tools": tools_holder[0] or [],
1408214112 "history_offset": _effective_history_offset,
1408314113 "last_prompt_tokens": _last_prompt_toks,
0 commit comments