@@ -227,7 +227,7 @@ def _default_run_state_validation_error(
227227 ),
228228 "1.17" : (
229229 "Persists Docker container labels and current-response generated-item ownership across "
230- "resume flows, including pending resumed Session writes."
230+ "resume flows, including pending resumed Session writes and terminal-unrecoverable runs ."
231231 ),
232232}
233233SUPPORTED_SCHEMA_VERSIONS = frozenset (SCHEMA_VERSION_SUMMARIES )
@@ -878,6 +878,13 @@ class RunState(Generic[TContext, TAgent]):
878878 _session_write_in_progress : bool = field (default = False , repr = False )
879879 """Live ownership guard; independent serialized copies require caller serialization."""
880880
881+ _terminal_unrecoverable : bool = field (default = False , repr = False )
882+ """Set once a final output, its guardrails, and its terminal hooks have all completed.
883+
884+ It closes the state for the window where the run owns an accepted result that no resume can
885+ reproduce, and it is cleared only once that turn is fully persisted.
886+ """
887+
881888 def __init__ (
882889 self ,
883890 context : RunContextWrapper [TContext ],
@@ -920,6 +927,7 @@ def __init__(
920927 self ._schema_version = CURRENT_SCHEMA_VERSION
921928 self ._pending_session_write = None
922929 self ._session_write_in_progress = False
930+ self ._terminal_unrecoverable = False
923931 from .agent_tool_state import get_agent_tool_state_scope
924932
925933 self ._agent_tool_state_scope_id = get_agent_tool_state_scope (context )
@@ -1909,6 +1917,8 @@ def to_json(
19091917 result ["current_turn_persisted_item_count" ] = self ._current_turn_persisted_item_count
19101918 if self ._pending_session_write is not None :
19111919 result ["pending_session_write" ] = copy .deepcopy (self ._pending_session_write )
1920+ if self ._terminal_unrecoverable :
1921+ result ["terminal_unrecoverable" ] = True
19121922 result ["trace" ] = self ._serialize_trace_data (
19131923 include_tracing_api_key = include_tracing_api_key
19141924 )
@@ -4383,6 +4393,13 @@ async def _build_run_state_from_json(
43834393 ):
43844394 raise validation_error_factory ("Run state pending Session write is invalid" , UserError )
43854395 state ._pending_session_write = copy .deepcopy (cast (_PendingSessionWrite , pending_write ))
4396+ terminal_unrecoverable = state_json .get ("terminal_unrecoverable" )
4397+ if terminal_unrecoverable is not None :
4398+ # An older label never wrote this marker, so honoring one would let a snapshot claim a
4399+ # resume boundary the schema it declares does not have.
4400+ if (schema_major , schema_minor ) < (1 , 17 ) or terminal_unrecoverable is not True :
4401+ raise validation_error_factory ("Run state terminal marker is invalid" , UserError )
4402+ state ._terminal_unrecoverable = True
43864403 serialized_policy = state_json .get ("reasoning_item_id_policy" )
43874404 if serialized_policy in {"preserve" , "omit" }:
43884405 state ._reasoning_item_id_policy = cast (Literal ["preserve" , "omit" ], serialized_policy )
@@ -5172,6 +5189,7 @@ def _clone_original_input(original_input: str | list[Any]) -> str | list[Any]:
51725189 "Run state agent not found in agent map" ,
51735190 "Run state pending_input must be a list" ,
51745191 "Run state pending Session write is invalid" ,
5192+ "Run state terminal marker is invalid" ,
51755193 "Run state references an agent identity that is not present in the restored graph" ,
51765194 (
51775195 "RunState context was serialized from a custom type; provide context_deserializer "
0 commit comments