@@ -155,6 +155,13 @@ class RunResult(RunResultBase):
155155 )
156156 _last_processed_response : ProcessedResponse | None = field (default = None , repr = False )
157157 """The last processed model response. This is needed for resuming from interruptions."""
158+ _tool_use_tracker_snapshot : dict [str , list [str ]] = field (default_factory = dict , repr = False )
159+ _current_turn_persisted_item_count : int = 0
160+ """Number of items from new_items already persisted to session for the
161+ current turn."""
162+ _original_input : str | list [TResponseInputItem ] | None = field (default = None , repr = False )
163+ """The original input from the first turn. Unlike `input`, this is never updated during the run.
164+ Used by to_state() to preserve the correct originalInput when serializing state."""
158165
159166 def __post_init__ (self ) -> None :
160167 self ._last_agent_ref = weakref .ref (self ._last_agent )
@@ -204,9 +211,12 @@ def to_state(self) -> Any:
204211 ```
205212 """
206213 # Create a RunState from the current result
214+ original_input_for_state = getattr (self , "_original_input" , None )
207215 state = RunState (
208216 context = self .context_wrapper ,
209- original_input = self .input ,
217+ original_input = original_input_for_state
218+ if original_input_for_state is not None
219+ else self .input ,
210220 starting_agent = self .last_agent ,
211221 max_turns = 10 , # This will be overridden by the runner
212222 )
@@ -217,6 +227,8 @@ def to_state(self) -> Any:
217227 state ._input_guardrail_results = self .input_guardrail_results
218228 state ._output_guardrail_results = self .output_guardrail_results
219229 state ._last_processed_response = self ._last_processed_response
230+ state ._current_turn_persisted_item_count = self ._current_turn_persisted_item_count
231+ state .set_tool_use_tracker_snapshot (self ._tool_use_tracker_snapshot )
220232
221233 # If there are interruptions, set the current step
222234 if self .interruptions :
@@ -279,11 +291,32 @@ class RunResultStreaming(RunResultBase):
279291 _output_guardrails_task : asyncio .Task [Any ] | None = field (default = None , repr = False )
280292 _stored_exception : Exception | None = field (default = None , repr = False )
281293
294+ _current_turn_persisted_item_count : int = 0
295+ """Number of items from new_items already persisted to session for the
296+ current turn."""
297+
298+ _stream_input_persisted : bool = False
299+ """Whether the input has been persisted to the session. Prevents double-saving."""
300+
301+ _original_input_for_persistence : list [TResponseInputItem ] = field (default_factory = list )
302+ """Original turn input before session history was merged, used for
303+ persistence (matches JS sessionInputOriginalSnapshot)."""
304+
282305 # Soft cancel state
283306 _cancel_mode : Literal ["none" , "immediate" , "after_turn" ] = field (default = "none" , repr = False )
284307
308+ _original_input : str | list [TResponseInputItem ] | None = field (default = None , repr = False )
309+ """The original input from the first turn. Unlike `input`, this is never updated during the run.
310+ Used by to_state() to preserve the correct originalInput when serializing state."""
311+ _tool_use_tracker_snapshot : dict [str , list [str ]] = field (default_factory = dict , repr = False )
312+ _state : Any = field (default = None , repr = False )
313+ """Internal reference to the RunState for streaming results."""
314+
285315 def __post_init__ (self ) -> None :
286316 self ._current_agent_ref = weakref .ref (self .current_agent )
317+ # Store the original input at creation time (it will be set via input field)
318+ if self ._original_input is None :
319+ self ._original_input = self .input
287320
288321 @property
289322 def last_agent (self ) -> Agent [Any ]:
@@ -508,9 +541,11 @@ def to_state(self) -> Any:
508541 ```
509542 """
510543 # Create a RunState from the current result
544+ # Use _original_input (the input from the first turn) instead of input
545+ # (which may have been updated during the run)
511546 state = RunState (
512547 context = self .context_wrapper ,
513- original_input = self .input ,
548+ original_input = self ._original_input if self . _original_input is not None else self . input ,
514549 starting_agent = self .last_agent ,
515550 max_turns = self .max_turns ,
516551 )
@@ -522,6 +557,8 @@ def to_state(self) -> Any:
522557 state ._output_guardrail_results = self .output_guardrail_results
523558 state ._current_turn = self .current_turn
524559 state ._last_processed_response = self ._last_processed_response
560+ state ._current_turn_persisted_item_count = self ._current_turn_persisted_item_count
561+ state .set_tool_use_tracker_snapshot (self ._tool_use_tracker_snapshot )
525562
526563 # If there are interruptions, set the current step
527564 if self .interruptions :
0 commit comments