
    Pmj8E                       U d Z ddlmZ ddlmZ ddlmZmZ ddlm	Z	m
Z
mZmZmZmZmZ  e            ZeZe G d d                      Ze G d d	                      Ze G d
 d                      Ze G d d                      Z G d d          ZdTdZdTdZdTdZ G d de          Z G d de          Zi d eddd e          d edd ee          d! edd"d# e          d$ edd%ee          d& ed'd(d) e          d* ed'd+d, e          d- ed'd.d/ e          d0 ed'd1d2 e          d3 ed'd4ee          d5 ed'd6ee          d7 ed'd8ee          d9 ed'd:d; e          d< ed'd=d> e          d? ed@dAdB e          dC ed@dDe e          dE ed@dFee          dG ed@dHdI e          dJ ed@dKe!e          iZ"dLe#dM<   dUdQZ$dVdRZ%dSS )Wu  Per-session gateway state consolidated into one container.

GatewayRunner historically carried ~19 separate ``Dict[str, ...]`` attributes
keyed by session_key, each with its own ad-hoc lifecycle.  Three failure
classes grew out of that shape:

1. Boundary drift — every conversation boundary carried a hand-copied
   pop-list that went stale when a new dict was added (#48031, #58403,
   #10702, #35809).  Mitigated by the ``_CONVERSATION_SCOPED_STATE`` registry,
   now structurally fixed: the fields live in one ``ConversationState``
   dataclass with a single ``clear()``.
2. Turn-release drift — ad-hoc ``del self._running_agents[key]`` sites that
   popped different subsets of the turn dicts.  Mitigated by
   ``_release_running_agent_state``, now ``TurnState.clear()``.
3. Wholesale-reset races — lazy-init paths like
   ``self._session_reasoning_overrides = {}`` replaced the ENTIRE dict,
   discarding concurrent sessions' entries when raced.  Structurally
   impossible now: state is per-session, resets touch one field of one
   ``SessionState``.

Scopes (placement follows where each dict is CLEARED today):

- ``SessionState.turn`` — reset at end of every running turn.
- ``SessionState.conversation`` — reset at conversation boundaries
  (/new, /resume, auto-reset, expiry, compression-exhausted reset).
- ``SessionState.persistent`` — own lifecycles (approval resolution, update
  prompt answer, native-image consumption); ``run_generation`` is monotonic
  and NEVER reset (#28686).

Entries in ``GatewayRunner._sessions`` are never evicted (matching the old
dicts, most of which also leaked empty/stale entries for dead sessions —
see the migration table in the consolidating commit).  Follow-up work may
add eviction of fully-default SessionStates.
    )annotations)MutableMapping)	dataclassfield)AnyCallableDictIteratorListOptionalTuplec                  p    e Zd ZU dZdZded<   dZded<   dZded<   dZded	<   dZ	ded
<   dZ
ded<   ddZdS )	TurnStateu  State scoped to one running gateway turn.

    Cleared by ``GatewayRunner._release_running_agent_state`` (via
    ``clear()``) at every site that ends a running turn.  ``lease_token`` /
    ``lease_generation`` are deliberately NOT cleared here — they are owned
    by ``_release_turn_lease`` (#64934), which must release the registry
    lease exactly once per acquiring turn.
    Nr   agent        float
started_tsleasebusy_ack_tslease_tokenzOptional[int]lease_generationreturnNonec                >    d| _         d| _        d| _        d| _        dS )a*  Reset the per-turn slot (agent / start ts / lease / busy-ack).

        Mirrors the exact clear set of the old _release_running_agent_state:
        _running_agents, _running_agents_ts, _active_session_leases (popped
        by the caller so it can call ``lease.release()``), _busy_ack_ts.
        Nr   )r   r   r   r   selfs    ;/home/thesage/.hermes/hermes-agent/gateway/session_state.pyclearzTurnState.clearM   s'     

    r   r   )__name__
__module____qualname____doc__r   __annotations__r   r   r   r   r   r    r   r   r   r   3   s           EJEK
 K&*****
 
 
 
 
 
r   r   c                      e Zd ZU dZdZded<   dZded<   dZded<   eZ	ded<   d	Z
d
ed<    ee          Zded<    ee          Zded<   dZded<   dZded<   ddZdS )ConversationStatezBState scoped to one conversation (survives turns, not boundaries).NOptional[Dict[str, Any]]model_overrideone_turn_restorereasoning_overrider   service_tier_override strlast_resolved_modeldefault_factoryz	List[Any]queued_events	List[str]sidecar_noteszOptional[Tuple[Any, ...]]ephemeral_pinOptional[str]vc_lastr   r   c                    d| _         d| _        d| _        t          | _        d| _        g | _        g | _        d| _        d| _	        dS )zReset every conversation-scoped field to its default.

        The structural successor of the ``_CONVERSATION_SCOPED_STATE``
        pop-loop: adding a field here means every boundary clears it
        automatically.
        Nr.   )
r*   r+   r,   _UNSET_TIERr-   r0   r3   r5   r6   r8   r   s    r   r   zConversationState.clearq   sO     # $"&%0"#% !r   r    )r!   r"   r#   r$   r*   r%   r+   r,   r:   r-   r0   r   listr3   r5   r6   r8   r   r&   r   r   r(   r(   Z   s         LL 04N3333155555377777!,,,,,!!!!!$uT:::M::::$uT:::M::::/3M3333!G!!!!     r   r(   c                  n    e Zd ZU dZdZded<   dZded<    ee          Z	d	ed
<   dZ
ded<   dZded<   dS )PersistentStateu   State with its own lifecycle — NOT cleared by turn or boundary resets
    wholesale (approvals/update prompts ARE cleared by the boundary
    *security* funnel, but individually, matching the old behavior).Nr)   	approvalsFboolupdate_prompt_pendingr1   r4   native_image_pathsr7   pending_command_textr   intrun_generation)r!   r"   r#   r$   r>   r%   r@   r   r;   rA   rB   rD   r&   r   r   r=   r=      s         H H
 +/I...."'''''$)E$$?$?$?????
 +/.... Nr   r=   c                  z    e Zd ZU dZ ee          Zded<    ee          Z	ded<    ee
          Zded<   d	S )
SessionStatez:All per-session gateway state, grouped by lifecycle scope.r1   r   turnr(   conversationr=   
persistentN)r!   r"   r#   r$   r   r   rG   r%   r(   rH   r=   rI   r&   r   r   rF   rF      ss         DDeI666D6666&+e<M&N&N&NLNNNN"'%"H"H"HJHHHHHHr   rF   c                      e Zd ZdZdZddZdS )
_FieldSpecz@Describes one legacy dict: scope attr, field name, absence test.scopenamedefault
is_presentrM   r/   rN   rO   Callable[[], Any]rP   Callable[[Any], bool]r   r   c                >    || _         || _        || _        || _        d S NrL   )r   rM   rN   rO   rP   s        r   __init__z_FieldSpec.__init__   s$     
	$r   N)
rM   r/   rN   r/   rO   rQ   rP   rR   r   r   )r!   r"   r#   r$   	__slots__rU   r&   r   r   rK   rK      s3        JJ:I
% 
% 
% 
% 
% 
%r   rK   vr   r   r?   c                
    | d uS rT   r&   rW   s    r   _present_not_nonerZ      s    D=r   c                     t          |           S rT   )r?   rY   s    r   _present_nonzeror\      s    77Nr   c                    | t           uS rT   r:   rY   s    r   _present_not_unsetr_      s    Kr   c                  ~    e Zd ZdZdZd"d	Zd#dZd$dZd%dZd&dZ	d'dZ
d(dZd)dZd*dZd+dZd,dZd,dZd-d Zd!S ).SessionFieldViewz>Live dict-like view of one SessionState field across sessions._runner_specrunnerr   specrK   r   r   c                "    || _         || _        d S rT   rb   )r   re   rf   s      r   rU   zSessionFieldView.__init__   s    


r   Dict[str, SessionState]c                D    | j         j                            d          pi S N	_sessionsrc   __dict__getr   s    r   rk   zSessionFieldView._sessions        |$((55;;r   staterF   c                f    t          t          || j        j                  | j        j                  S rT   )getattrrd   rM   rN   r   rp   s     r   _valuezSessionFieldView._value   s%    wudj&677IIIr   keyr/   c                    |                                                      |          }|t          |          |                     |          }| j                            |          st          |          |S rT   )rk   rn   KeyErrorrt   rd   rP   )r   ru   rp   values       r   __getitem__zSessionFieldView.__getitem__   sj      $$S))=3--E""z$$U++ 	 3--r   rx   c                    | j                             |          }t          t          || j        j                  | j        j        |           d S rT   )rc   _session_statesetattrrr   rd   rM   rN   )r   ru   rx   rp   s       r   __setitem__zSessionFieldView.__setitem__   sB    ++C00tz/00$*/5IIIIIr   c                d   |                                                      |          }|-| j                            |                     |                    st          |          t          t          || j        j                  | j        j	        | j        
                                           d S rT   )rk   rn   rd   rP   rt   rw   r|   rr   rM   rN   rO   r   ru   rp   s      r   __delitem__zSessionFieldView.__delitem__   s      $$S))=
 5 5dkk%6H6H I I=3--E4:+,,JOJ  	
 	
 	
 	
 	
r   Iterator[str]c              #     K   t          |                                                                           D ]6\  }}| j                            |                     |                    r|V  7d S rT   )r;   rk   itemsrd   rP   rt   r   s      r   __iter__zSessionFieldView.__iter__   so      t~~//557788 	 	JCz$$T[[%7%788 				 	r   rC   c                4    t          d | D                       S )Nc              3     K   | ]}d V  dS    Nr&   .0_s     r   	<genexpr>z+SessionFieldView.__len__.<locals>.<genexpr>   "      ##1######r   sumr   s    r   __len__zSessionFieldView.__len__       ##d######r   objectr?   c                    |                                                      |          }|d uo,| j                            |                     |                    S rT   )rk   rn   rd   rP   rt   r   s      r   __contains__zSessionFieldView.__contains__   sK      $$S))D NTZ%:%:4;;u;M;M%N%NNr   c                   t          |                                                                           D ]L}t          t	          || j        j                  | j        j        | j                                                   Md S rT   )	r;   rk   valuesr|   rr   rd   rM   rN   rO   rs   s     r   r   zSessionFieldView.clear  sz    $..**113344 	 	Etz/00

""$$   	 	r   otherc                    t          |t          t          f          r2t          |                                           t          |          k    S t          S rT   
isinstancedictr   r   NotImplementedr   r   s     r   __eq__zSessionFieldView.__eq__  @    edN344 	5

%%e44r   c                R    |                      |          }|t          u rt          n| S rT   r   r   r   r   results      r   __ne__zSessionFieldView.__ne__  )    U##!'>!9!9~~6zIr   c           	         d| j         j         d| j         j         dt          |                                           dS )NzSessionFieldView(.z, ))rd   rM   rN   r   r   r   s    r   __repr__zSessionFieldView.__repr__  s<    `4:#3``djo``djjllI[I[````r   N)re   r   rf   rK   r   r   r   rh   )rp   rF   r   r   )ru   r/   r   r   )ru   r/   rx   r   r   r   )ru   r/   r   r   )r   r   r   rC   )ru   r   r   r?   r    r   r   r   r?   )r   r/   )r!   r"   r#   r$   rV   rU   rk   rt   ry   r}   r   r   r   r   r   r   r   r   r&   r   r   ra   ra      s9       HH$I   
< < < <J J J J   J J J J
 
 
 
   
$ $ $ $O O O O      
J J J Ja a a a a ar   ra   c                  v    e Zd ZdZdZddZdd	Zedd            ZddZ	ddZ
d dZd!dZd"dZd#dZd#dZdS )$TurnLeaseTokenViewaM  Legacy view of ``_turn_lease_tokens``: keyed by (session_key, generation).

    The pair now lives on ``TurnState.lease_token`` / ``lease_generation``;
    at most one held token exists per session key (the lease registry
    serializes acquisition per session), so the single slot is equivalent to
    the old tuple-keyed dict.
    rc   re   r   r   r   c                    || _         d S rT   r   )r   re   s     r   rU   zTurnLeaseTokenView.__init__#  s    r   rh   c                D    | j         j                            d          pi S rj   rl   r   s    r   rk   zTurnLeaseTokenView._sessions&  ro   r   ru   Tuple[str, int]c                    t          | t                    rt          |           dk    rt          |           | d         | d         fS )N   r   r   )r   tuplelenrw   )ru   s    r   _splitzTurnLeaseTokenView._split)  s@    #u%% 	 SQ3--1vs1v~r   c                    |                      |          \  }}|                                                     |          }||j        j        |j        j        |k    rt          |          |j        j        S rT   r   rk   rn   rG   r   r   rw   r   ru   session_key
generationrp   s        r   ry   zTurnLeaseTokenView.__getitem__/  sl    "&++c"2"2Z  $$[11Mz%-z*j883--z%%r   rx   c                    |                      |          \  }}| j                            |          }||j        _        ||j        _        d S rT   )r   rc   r{   rG   r   r   )r   ru   rx   r   r   rp   s         r   r}   zTurnLeaseTokenView.__setitem__:  sG    "&++c"2"2Z++K88!&
&0
###r   c                   |                      |          \  }}|                                                     |          }||j        j        |j        j        |k    rt          |          d |j        _        d |j        _        d S rT   r   r   s        r   r   zTurnLeaseTokenView.__delitem__@  s{    "&++c"2"2Z  $$[11Mz%-z*j883--!%
&*
###r   Iterator[Tuple[str, Any]]c              #     K   t          |                                                                           D ]!\  }}|j        j        ||j        j        fV  "d S rT   )r;   rk   r   rG   r   r   r   s      r   r   zTurnLeaseTokenView.__iter__L  sc      t~~//557788 	9 	9JCz%1EJ78888	9 	9r   rC   c                4    t          d | D                       S )Nc              3     K   | ]}d V  dS r   r&   r   s     r   r   z-TurnLeaseTokenView.__len__.<locals>.<genexpr>R  r   r   r   r   s    r   r   zTurnLeaseTokenView.__len__Q  r   r   r   r   r?   c                    t          |t          t          f          r2t          |                                           t          |          k    S t          S rT   r   r   s     r   r   zTurnLeaseTokenView.__eq__T  r   r   c                R    |                      |          }|t          u rt          n| S rT   r   r   s      r   r   zTurnLeaseTokenView.__ne__Y  r   r   N)re   r   r   r   r   )ru   r   r   r   )ru   r   r   r   )ru   r   rx   r   r   r   )ru   r   r   r   )r   r   r   r   )r!   r"   r#   r$   rV   rU   rk   staticmethodr   ry   r}   r   r   r   r   r   r&   r   r   r   r     s          I   < < < <    \
	& 	& 	& 	&1 1 1 1
+ 
+ 
+ 
+9 9 9 9
$ $ $ $   
J J J J J Jr   r   _running_agentsrG   r   c                     d S rT   r&   r&   r   r   <lambda>r   a  s    4 r   _running_agents_tsr   _active_session_leasesr   c                     d S rT   r&   r&   r   r   r   r   c  s    $ r   _busy_ack_tsr   _session_model_overridesrH   r*   c                     d S rT   r&   r&   r   r   r   r   f  s    $ r    _pending_one_turn_model_restoresr+   c                     d S rT   r&   r&   r   r   r   r   i  s    D r   _session_reasoning_overridesr,   c                     d S rT   r&   r&   r   r   r   r   l      d r   _session_service_tier_overridesr-   c                     t           S rT   r^   r&   r   r   r   r   o  s     r   _last_resolved_modelr0   _queued_eventsr3   _pending_turn_sidecar_notesr5   _session_ephemeral_pinr6   c                     d S rT   r&   r&   r   r   r   r   y  s     r   _session_vc_lastr8   c                     d S rT   r&   r&   r   r   r   r   |      4 r   _pending_approvalsrI   r>   c                     d S rT   r&   r&   r   r   r   r     r   r   _update_prompt_pendingr@   &_pending_native_image_paths_by_sessionrA   _pending_messagesrB   c                     d S rT   r&   r&   r   r   r   r     r   r   _session_run_generationrD   zDict[str, _FieldSpec]LEGACY_FIELD_SPECS	attr_namer/   propertyc           
         t           |          dfd}dfd	}dfd
}t          |||dj         dj         d          S )a$  Build a legacy dict-shaped @property for one migrated attribute.

    Getter returns a live :class:`SessionFieldView`; setter accepts a plain
    dict (the ubiquitous test pattern ``runner._X = {...}``), resetting the
    field on every known session and then applying the given entries.
    r   r   r   ra   c                $    t          |           S rT   )ra   r   rf   s    r   fgetz"legacy_dict_property.<locals>.fget  s    d+++r   mappingr)   r   c                    t          |           }|                                 |pi                                 D ]
\  }}|||<   d S rT   )ra   r   r   )r   r   viewru   rx   rf   s        r   fsetz"legacy_dict_property.<locals>.fset  sW    d++

"=b//11 	 	JCDII	 	r   c                L    t          |                                            d S rT   )ra   r   r   s    r   fdelz"legacy_dict_property.<locals>.fdel  s'     	t$$**,,,,,r   z#Legacy dict view over SessionState.r   z= (kept for tests that access the pre-SessionState attribute).doc)r   r   r   ra   )r   r   r   r)   r   r   r   r   r   r   )r   r   rM   rN   )r   r   r   r   rf   s       @r   legacy_dict_propertyr     s     i(D, , , , , ,     - - - - - -
 K$* K Kty K K K   r   c                 @    dd} dd	}dd
}t          | ||d          S )zFLegacy (session_key, generation)-keyed view of held turn-lease tokens.r   r   r   r   c                     t          |           S rT   )r   r   s    r   r   z)legacy_lease_token_property.<locals>.fget  s    !$'''r   r   $Optional[Dict[Tuple[str, int], Any]]r   c                    t          |           }t          |          D ]}||= |pi                                 D ]
\  }}|||<   d S rT   )r   r;   r   )r   r   r   ru   rx   s        r   r   z)legacy_lease_token_property.<locals>.fset  se    !$'':: 	 	CS		"=b//11 	 	JCDII	 	r   c                N    t          |           }t          |          D ]}||= d S rT   )r   r;   )r   r   ru   s      r   r   z)legacy_lease_token_property.<locals>.fdel  s7    !$'':: 	 	CS			 	r   z=Legacy (session_key, generation)-keyed turn-lease token view.r   )r   r   r   r   )r   r   r   r   r   r   r   )r   )r   r   r   s      r   legacy_lease_token_propertyr     sc    ( ( ( (      
 K	   r   N)rW   r   r   r?   )r   r/   r   r   )r   r   )&r$   
__future__r   collections.abcr   dataclassesr   r   typingr   r   r	   r
   r   r   r   r   r:   SERVICE_TIER_UNSETr   r(   r=   rF   rK   rZ   r\   r_   ra   r   r   r/   r;   r?   rC   r   r%   r   r   r&   r   r   <module>r     s  ! ! !F # " " " " " * * * * * * ( ( ( ( ( ( ( ( G G G G G G G G G G G G G G G G G G
 fhh !  # # # # # # # #L % % % % % % % %P        * I I I I I I I I,% % % % % % % %$             Ga Ga Ga Ga Ga~ Ga Ga GaTCJ CJ CJ CJ CJ CJ CJ CJP--zz&'<<ARSS--**V\5BRSS-- jj,,HYZZ-- JJv}e=MNN	--
 

(,,8I! !-- '

*LL:K) )-- #JJ,ll<M% %-- &zz/1D1DFX( (--" JJ-s4D #--( jj$HXYY)--* "::/?$ $+--0 jj7H 1--6 

	<<1B 7--< **k<<1B =--B jj-t5E C--H -jj*D2B/ /I--N ,ll<M O--T zz&-=   U-- --  - - - -`   D     r   