
    sjJ                       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    9/home/agent/.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                  |    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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hygiene_failure_streak)r!   r"   r#   r$   r>   r%   r@   r   r;   rA   rB   rD   rE   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   rH   r%   r(   rI   r=   rJ   r&   r   r   rG   rG      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   rG   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_presentrN   r/   rO   rP   Callable[[], Any]rQ   Callable[[Any], bool]r   r   c                >    || _         || _        || _        || _        d S NrM   )r   rN   rO   rP   rQ   s        r   __init__z_FieldSpec.__init__   s$     
	$r   N)
rN   r/   rO   r/   rP   rR   rQ   rS   r   r   )r!   r"   r#   r$   	__slots__rV   r&   r   r   rL   rL      s3        JJ:I
% 
% 
% 
% 
% 
%r   rL   vr   r   r?   c                
    | d uS rU   r&   rX   s    r   _present_not_noner[      s    D=r   c                     t          |           S rU   )r?   rZ   s    r   _present_nonzeror]      s    77Nr   c                    | t           uS rU   r:   rZ   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   specrL   r   r   c                "    || _         || _        d S rU   rc   )r   rf   rg   s      r   rV   zSessionFieldView.__init__   s    


r   Dict[str, SessionState]c                D    | j         j                            d          pi S N	_sessionsrd   __dict__getr   s    r   rl   zSessionFieldView._sessions        |$((55;;r   staterG   c                f    t          t          || j        j                  | j        j                  S rU   )getattrre   rN   rO   r   rq   s     r   _valuezSessionFieldView._value   s%    wudj&677IIIr   keyr/   c                    |                                                      |          }|t          |          |                     |          }| j                            |          st          |          |S rU   )rl   ro   KeyErrorru   re   rQ   )r   rv   rq   values       r   __getitem__zSessionFieldView.__getitem__   sj      $$S))=3--E""z$$U++ 	 3--r   ry   c                    | j                             |          }t          t          || j        j                  | j        j        |           d S rU   )rd   _session_statesetattrrs   re   rN   rO   )r   rv   ry   rq   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 rU   )rl   ro   re   rQ   ru   rx   r}   rs   rN   rO   rP   r   rv   rq   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 rU   )r;   rl   itemsre   rQ   ru   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 rU   )rl   ro   re   rQ   ru   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 rU   )	r;   rl   valuesr}   rs   re   rN   rO   rP   rt   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 rU   
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 rU   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, ))re   rN   rO   r   r   r   s    r   __repr__zSessionFieldView.__repr__&  s<    `4:#3``djo``djjllI[I[````r   N)rf   r   rg   rL   r   r   r   ri   )rq   rG   r   r   )rv   r/   r   r   )rv   r/   ry   r   r   r   )rv   r/   r   r   )r   r   r   rC   )rv   r   r   r?   r    r   r   r   r?   )r   r/   )r!   r"   r#   r$   rW   rV   rl   ru   rz   r~   r   r   r   r   r   r   r   r   r&   r   r   rb   rb      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   rb   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.
    rd   rf   r   r   r   c                    || _         d S rU   r   )r   rf   s     r   rV   zTurnLeaseTokenView.__init__5  s    r   ri   c                D    | j         j                            d          pi S rk   rm   r   s    r   rl   zTurnLeaseTokenView._sessions8  rp   r   rv   Tuple[str, int]c                    t          | t                    rt          |           dk    rt          |           | d         | d         fS )N   r   r   )r   tuplelenrx   )rv   s    r   _splitzTurnLeaseTokenView._split;  s@    #u%% 	 SQ3--1vs1v~r   c                    |                      |          \  }}|                                                     |          }||j        j        |j        j        |k    rt          |          |j        j        S rU   r   rl   ro   rH   r   r   rx   r   rv   session_key
generationrq   s        r   rz   zTurnLeaseTokenView.__getitem__A  sl    "&++c"2"2Z  $$[11Mz%-z*j883--z%%r   ry   c                    |                      |          \  }}| j                            |          }||j        _        ||j        _        d S rU   )r   rd   r|   rH   r   r   )r   rv   ry   r   r   rq   s         r   r~   zTurnLeaseTokenView.__setitem__L  sG    "&++c"2"2Z++K88!&
&0
###r   c                   |                      |          \  }}|                                                     |          }||j        j        |j        j        |k    rt          |          d |j        _        d |j        _        d S rU   r   r   s        r   r   zTurnLeaseTokenView.__delitem__R  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 rU   )r;   rl   r   rH   r   r   r   s      r   r   zTurnLeaseTokenView.__iter__^  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>d  r   r   r   r   s    r   r   zTurnLeaseTokenView.__len__c  r   r   r   r   r?   c                    t          |t          t          f          r2t          |                                           t          |          k    S t          S rU   r   r   s     r   r   zTurnLeaseTokenView.__eq__f  r   r   c                R    |                      |          }|t          u rt          n| S rU   r   r   s      r   r   zTurnLeaseTokenView.__ne__k  r   r   N)rf   r   r   r   r   )rv   r   r   r   )rv   r   r   r   )rv   r   ry   r   r   r   )rv   r   r   r   )r   r   r   r   )r!   r"   r#   r$   rW   rV   rl   staticmethodr   rz   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_agentsrH   r   c                     d S rU   r&   r&   r   r   <lambda>r   s  s    4 r   _running_agents_tsr   _active_session_leasesr   c                     d S rU   r&   r&   r   r   r   r   u  s    $ r   _busy_ack_tsr   _session_model_overridesrI   r*   c                     d S rU   r&   r&   r   r   r   r   x  s    $ r    _pending_one_turn_model_restoresr+   c                     d S rU   r&   r&   r   r   r   r   {  s    D r   _session_reasoning_overridesr,   c                     d S rU   r&   r&   r   r   r   r   ~      d r   _session_service_tier_overridesr-   c                     t           S rU   r_   r&   r   r   r   r     s     r   _last_resolved_modelr0   _queued_eventsr3   _pending_turn_sidecar_notesr5   _session_ephemeral_pinr6   c                     d S rU   r&   r&   r   r   r   r     s     r   _session_vc_lastr8   c                     d S rU   r&   r&   r   r   r   r         4 r   _pending_approvalsrJ   r>   c                     d S rU   r&   r&   r   r   r   r     r   r   _update_prompt_pendingr@   &_pending_native_image_paths_by_sessionrA   _pending_messagesrB   c                     d S rU   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   rb   c                $    t          |           S rU   )rb   r   rg   s    r   fgetz"legacy_dict_property.<locals>.fget  s    d+++r   mappingr)   r   c                    t          |           }|                                 |pi                                 D ]
\  }}|||<   d S rU   )rb   r   r   )r   r   viewrv   ry   rg   s        r   fsetz"legacy_dict_property.<locals>.fset  sW    d++

"=b//11 	 	JCDII	 	r   c                L    t          |                                            d S rU   )rb   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   rb   )r   r   r   r)   r   r   r   r   r   r   )r   r   rN   rO   )r   r   r   r   rg   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 rU   )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 rU   )r   r;   r   )r   r   r   rv   ry   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 rU   )r   r;   )r   r   rv   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)rX   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=   rG   rL   r[   r]   r`   rb   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 $$ $$ $$ $$ $$ $$ $$ $$N 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   