+
    jHàjf$  ã                   ó¼   € R t ^ RIt^ RIt^ RIt^ RIHtHt ]P                  ! ]4      t	Rt
RtR R lt ! R R]4      t ! R	 R
4      t ! R R4      t ! R R4      tR# )uÛ  Per-session turn lease â€” serializes the [load history -> run -> flush] region.

Busy guards are keyed by ROUTING KEY but the transcript is owned by SESSION_ID, and
``switch_session()`` makes key->id many-to-one (/resume from a second chat, CLI-continuity,
delegation pinning, topic tip-walks), so two keys could interleave flushes on one transcript
(``user;user`` wedge). The lease serializes per RESOLVED session_id: acquired right before the
transcript load, released in the dispatch layer's ``finally``; identity-checked release; a
timed-out waiter fails CLOSED (:class:`TurnLeaseTimeoutError`); only idle entries evict. Limits:
CLI-continuity processes are outside this lock; mid-turn rotation alias is closed by ``rebind``.
N)ÚDictÚOptionali   g      @c                ó(   € V ^8„  d   QhRRR\         /# )é   ÚholderzOptional[TurnLeaseToken]Úreturn)Útuple)Úformats   "Ú7/home/ubuntu/.hermes/hermes-agent/gateway/turn_lease.pyÚ__annotate__r      s   € ÷ Kñ KÐ3ð K¼ñ Kó    c                 óF   € V '       d   V P                   V P                  3# R# )Ú?)r   r   )Ú	owner_keyÚ
generation)r   s   &r
   Ú_holder_descr      s    € ß4:ˆF×Ñ˜f×/Ñ/Ð0ÐJÀ
ÐJr   c                   óB   a a€ ] tR t^ t oRtV3R lV 3R lltRtVtV ;t# )ÚTurnLeaseTimeoutErrorzXLease held for the full wait budget; fail-closed: caller must not enter the turn region.c                ó2   <€ V ^8„  d   QhRS[ RS[ RS[RS[/# )r   Ú
session_idr   r   Úwait_seconds)ÚstrÚintÚfloat)r	   Ú__classdict__s   "€r
   r   Ú"TurnLeaseTimeoutError.__annotate__#   s0   ø€ ÷ Yñ Y¡3ð Y±cð YÁsð YÑZ_ñ Yr   c               óz   <€ WuV n         V n        W4uV n        V n        \        SV `  R VR RV RV RV R2	4       R# )z turn lease wait timed out after z.0fzs on session z for routing key z (gen Ú)N)r   r   r   r   ÚsuperÚ__init__)Úselfr   r   r   r   Ú	__class__s   &&$$$€r
   r   ÚTurnLeaseTimeoutError.__init__#   sU   ø€ Ø*4Ð'ˆŒ˜œØ-7Ð*ˆŒ˜Ô*Ü‰ÑÐ;¸LÈÐ;MÈ]Ø&˜<Ð'8¸¸À6È*ÈÐUVðXö 	Yr   )r   r   r   r   )	Ú__name__Ú
__module__Ú__qualname__Ú__firstlineno__Ú__doc__r   Ú__static_attributes__Ú__classdictcell__Ú__classcell__)r!   r   s   @@r
   r   r       s   ù‡ € Ùb÷Y÷ Yó Yr   r   c                   óL   a € ] tR t^*t o RtRtV 3R lR ltV 3R lR ltRtV t	R# )ÚTurnLeaseTokenzgHeld-lease handle from :meth:`SessionTurnLeaseRegistry.acquire`; ``released`` makes
release idempotent.c          
      ó4   <€ V ^8„  d   QhRS[ RS[ RS[RRRR/# )r   r   r   r   ÚleaseÚ_SessionLeaser   N)r   r   )r	   r   s   "€r
   r   ÚTurnLeaseToken.__annotate__0   s2   ø€ ÷ ñ ¡3ð ±3ð ÁCð ÐP_ð Ðdhñ r   c                óJ   € WVuV n         V n        V n        R V n        W@n        R# )FN)r   r   r   Úreleasedr.   )r    r   r   r   r.   s   &&&&&r
   r   ÚTurnLeaseToken.__init__0   s%   € Ø;EÐR\Ð8ˆŒ˜œ¨¬ØˆŒð Ž
r   c                ó    <€ V ^8„  d   QhRS[ /# ©r   r   )r   )r	   r   s   "€r
   r   r0   7   s   ø€ ÷ Lñ L™#ñ Lr   c           	     ót   € R V P                   : RV P                  : RV P                   RV P                   R2	# )zTurnLeaseToken(session_id=z, owner_key=z, generation=z, released=r   )r   r   r   r2   ©r    s   &r
   Ú__repr__ÚTurnLeaseToken.__repr__7   sA   € Ø,¨T¯_©_Ñ,?¸|ÈDÏNÉNÑK]ð ^Ø"Ÿo™oÐ.¨k¸$¿-¹-¸ÈðKð 	Lr   )r   r.   r   r2   r   N)
r#   r$   r%   r&   r'   Ú	__slots__r   r8   r(   r)   ©r   s   @r
   r,   r,   *   s(   ø‡ € ñð O€I÷ð ÷Lö Lr   r,   c                   óR   a € ] tR t^<t o RtV 3R lR lt]V 3R lR l4       tRtV t	R# )r/   c                ó   <€ V ^8„  d   QhRR/# ©r   r   N© )r	   r   s   "€r
   r   Ú_SessionLease.__annotate__?   s   ø€ ÷ Vñ V˜$ñ Vr   c                óš   € \         P                  ! 4       V n        R V n        R\        P                  ! 4       ^ uV n        V n        V n        R # )Nç        )ÚasyncioÚLockÚlockr   ÚtimeÚacquired_atÚ	last_usedÚpending_acquiresr7   s   &r
   r   Ú_SessionLease.__init__?   s5   € Ü—L’L“NˆŒ	Ø04ˆŒØBEÄtÇyÂyÃ{ÐTUÐ?ˆÔ˜$œ.¨$Ö*?r   c                ó    <€ V ^8„  d   QhRS[ /# r5   )Úbool)r	   r   s   "€r
   r   r@   E   s   ø€ ÷ ]ñ ]‘dñ ]r   c                óš   € V P                   RJ ;'       d7    V P                  P                  4       '       * ;'       d    V P                  ^ 8H  # )z/True when evictable: nobody holds or awaits it.N)r   rE   ÚlockedrI   r7   s   &r
   ÚidleÚ_SessionLease.idleD   s?   € ð �{‰{˜dÐ"×\Ð\¨4¯9©9×+;Ñ+;Ó+=Ô'=×\Ð\À$×BWÑBWÐ[\ÑB\Ð\r   )rG   r   rH   rE   rI   N)
r#   r$   r%   r&   r:   r   ÚpropertyrO   r(   r)   r;   s   @r
   r/   r/   <   s,   ø‡ € ØR€I÷Vð Vð
 ÷]ó ö]r   r/   c                   óž   a € ] tR t^Jt o Rt]3V 3R lR lltV 3R lR ltV 3R lR ltRR	/V 3R
 lR llt	V 3R lR lt
V 3R lR ltRtV tR	# )ÚSessionTurnLeaseRegistryz°Asyncio lease per resolved session_id. Process-local, single-event-loop by design (same
visibility scope as the routing-key guards it extends); call only from the gateway loop.c                ó$   <€ V ^8„  d   QhRS[ RR/# )r   Úmax_entriesr   N)r   )r	   r   s   "€r
   r   Ú%SessionTurnLeaseRegistry.__annotate__N   s   ø€ ÷ 5ñ 5¡Cð 5Àñ 5r   c                óH   € / V n         \        ^\        V4      4      V n        R# )é   N)Ú_leasesÚmaxr   Ú_max_entries)r    rU   s   &&r
   r   Ú!SessionTurnLeaseRegistry.__init__N   s   € Ø13ˆŒÜ ¤3 {Ó#3Ó4ˆÖr   c                ó&   <€ V ^8„  d   QhRS[ RS[/# )r   r   r   )r   r/   )r	   r   s   "€r
   r   rV   R   s   ø€ ÷ ñ ©ð ±ñ r   c                óÈ   € V P                   P                  V4      ;pf)   V P                  4        \        4       ;q P                   V&   \        P                  ! 4       Vn        V# ©N)rY   ÚgetÚ_evict_idler/   rF   rH   )r    r   r.   s   && r
   Ú_get_or_createÚ'SessionTurnLeaseRegistry._get_or_createR   sM   € Ø—\‘\×%Ñ% jÓ1Ð1ˆEÒ:Ø×ÑÔÜ/<«Ð>ˆE—L‘L Ñ,ÜŸ)š)›+ˆŒØˆr   c                ó   <€ V ^8„  d   QhRR/# r>   r?   )r	   r   s   "€r
   r   rV   Y   s   ø€ ÷ (ñ (˜Tñ (r   c                ó$  a € \        S P                  4      S P                  ,
          ^,           ;p^ 8:  d   R# \        R S P                  P	                  4        4       V 3R lR7      pVRV  F  pS P                  P                  VR4       K!  	  R# )zVDrop oldest idle entries to fit a new lease under the cap; never a held/contended one.Nc              3   óR   "  € T F  w  rVP                   '       g   K  Vx € K  	  R # 5ir_   )rO   )Ú.0ÚsidÚls   &  r
   Ú	<genexpr>Ú7SessionTurnLeaseRegistry._evict_idle.<locals>.<genexpr>]   s   é € ÐHÑ)=™v˜sÀÇÅ—s’sÓ)=ùs   ‚'�
'c                 ó>   <€ SP                   V ,          P                  # r_   )rY   rH   )rh   r    s   &€r
   Ú<lambda>Ú6SessionTurnLeaseRegistry._evict_idle.<locals>.<lambda>^   s   ø€  d§l¡l°3Õ&7×&AÒ&Ar   )Úkey)ÚlenrY   r[   ÚsortedÚitemsÚpop)r    ÚoverflowrO   rh   s   f   r
   ra   Ú$SessionTurnLeaseRegistry._evict_idleY   ss   ø€ ä˜DŸL™LÓ)¨D×,=Ñ,=Õ=ÀÕAÐAˆHÀaÔGÙÜÑH¨¯©×);Ñ);Ô)=ÓHÜAôCˆà˜	˜“?ˆCØ�L‰L×Ñ˜S $Ö'ó #r   ÚtimeoutNc                óX   <€ V ^8„  d   QhRS[ RS[ RS[RS[S[,          RS[S[,          /# )r   r   r   r   rv   r   )r   r   r   r   r,   )r	   r   s   "€r
   r   rV   b   s?   ø€ ÷ 'ñ 'Ùð'Ù-0ð'Ù>Að'ÙLTÑUZÍOð'á	‘.Õ	!ñ'r   c             ƒ  óº  "  € V'       g   R# V'       d   V^ 8”  d   \        V4      M\        pV P                  V4      p\        W\	        V4      VR7      pVP
                  P                  4       '       dh   \        P                  ! RWV.\        VP                  4      OVP                  '       d'   \        P                  ! 4       VP                  ,
          MRN5!   V;P                  ^,          un         \        P                  ! VP
                  P!                  4       VR7      G Rj  x€L
   T;P                  ^,          un        Yvn
        \        P                  ! 4       ;Tn        Tn        T#  LG  \"         d=    \        P$                  ! RYQY#.\        TP                  4      O5!   \'        YY5R7      Rhi ; i  T;P                  ^,          un        i ; i5i)z�Acquire the lease for ``session_id``, waiting if held. Raises
:class:`TurnLeaseTimeoutError` when the wait budget expires; None for a falsy id.N)r.   uù   turn lease contention on session %s: routing key %s (gen %s) waiting behind in-flight turn held by routing key %s (gen %s, held %.0fs) â€” two routing keys are mapped to one session_id (#64934); serializing this turn behind the previous turn's flush)rv   uÊ   turn lease wait timed out after %.0fs on session %s (waiter: routing key %s gen %s; holder: routing key %s gen %s) â€” failing closed: refusing to run this turn UNSERIALIZED against the still-held lease)r   r   r   g      ð¿)r   ÚDEFAULT_LEASE_WAITrb   r,   r   rE   rN   ÚloggerÚwarningr   r   rG   rF   rI   rC   Úwait_forÚacquireÚTimeoutErrorÚerrorr   rH   )r    r   r   r   rv   Úwaitr.   Útokens   &&$$$   r
   r}   Ú SessionTurnLeaseRegistry.acquireb   s�  é € ÷
 Ùß!(¨W°q¬[Œu�WŒ~Ô>PˆØ×#Ñ# JÓ/ˆÜ˜z´c¸*³oÈUÔSˆØ�:‰:×Ñ×ÒÜ�NŠNðð  zðPô
 5AÀÇÁÓ4NðPð 49×3D×3DÐ3D”—	’	“˜e×/Ñ/Ö/È$ôPð 	×Ò !Õ#Õð	(Ü×"Ò" 5§:¡:×#5Ñ#5Ó#7ÀÔF×FÑFð ×"Ò" aÕ'Õ"ð ŒÜ.2¯iªi«kÐ9ˆÔ˜EœOØˆñ GøÜô 	eÜ�LŠLð<ð  )ð	Vô ;GÀuÇ|Á|Ó:Tô	Vô
 (Ø¸Jô[Ø`dðeð	eûð ×"Ò" aÕ'Ö"üsC   ‚G•C GÃ63E3 Ä)E1Ä*E3 Ä/AGÅ1E3 Å3AF:Æ:F= Æ=GÇGc                ó<   <€ V ^8„  d   QhRS[ S[,          RS[RS[/# )r   r�   Únew_session_idr   )r   r,   r   rL   )r	   r   s   "€r
   r   rV   ‹   s'   ø€ ÷ ñ ™H¡^Õ4ð Ácð Édñ r   c                ó
  € Ve+   VP                   '       g   V'       d   W!P                  8X  d   R# VP                  pVP                  VJd   R# V P                  P                  V4      pVeh   WCJdc   VP                  '       gQ   \        P                  ! RVP                  W!P                  VP                  .\        VP                  4      OVN5!   R# W0P                  V&   \        P                  ! 4       Vn        W!n        R# )aE  Alias a HELD lease onto ``new_session_id`` after mid-turn rotation (compression) so the
flush target stays serialized: the SAME ``_SessionLease`` is registered under the new id
(old mapping idle-evicts later), only the holder may rebind, the token follows. A live
lease on the new id: log loudly, keep the old id (fail-open).Fu  turn lease rebind blocked: session %s rotated to %s mid-turn (holder: routing key %s gen %s) but the target session's lease is already live (holder: routing key %s gen %s) â€” keeping the lease on the old id; transcript writes on %s may interleave (#64934 rotation-alias edge)T)r2   r   r.   r   rY   r`   rO   rz   r{   r   r   r   rF   rH   )r    r�   r„   r.   Úexistings   &&&  r
   ÚrebindÚSessionTurnLeaseRegistry.rebind‹   sÒ   € ð
 ŠM˜UŸ^Ÿ^˜^·>Ø!×%5Ñ%5Ô5ÙØ—‘ˆØ�<‰<˜uÓ$ÙØ—<‘<×#Ñ# NÓ3ˆØÒ HÓ$9À(Ç-Ç-À-Ü�NŠNð:ð × Ñ  .·/±/À5×CSÑCSð@ô ˜hŸo™oÓ.ð@ð 1?ô@ñ Ø',�‰�^Ñ$ÜŸ)š)›+ˆŒØ)ÔÙr   c                ó6   <€ V ^8„  d   QhRS[ S[,          RS[/# )r   r�   r   )r   r,   rL   )r	   r   s   "€r
   r   rV   ¥   s    ø€ ÷ ñ ™X¡nÕ5ð ¹$ñ r   c                ó²  € Ve   VP                   '       d   R# RVn         VP                  pVP                  VJd9   \        P	                  RVP
                  VP                  VP                  4       R# RR\        P                  ! 4       uVn        Vn	        Vn
        VP                  P                  4       '       d   VP                  P                  4        R# )z®Release ``token``'s lease. Idempotent; True only when this exact token was the current
holder (a re-release or a stale token whose slot went to a newer turn is a safe no-op).NFTzYturn lease release skipped on session %s: token (key %s gen %s) is not the current holderrB   )r2   r.   r   rz   Údebugr   r   r   rF   rG   rH   rE   rN   Úrelease)r    r�   r.   s   && r
   rŒ   Ú SessionTurnLeaseRegistry.release¥   s    € ð Š=˜EŸNŸN˜NÙØˆŒØ—‘ˆØ�<‰<˜uÓ$Ü�L‰Lð .Ø/4×/?Ñ/?ÀÇÁÐRW×RbÑRbôdáØ;?ÀÄdÇiÂiÃkÐ8ˆŒ�eÔ'¨¬Ø�:‰:×Ñ×ÒØ�J‰J×ÑÔ Ùr   )rY   r[   )r#   r$   r%   r&   r'   ÚDEFAULT_MAX_LEASESr   rb   ra   r}   r‡   rŒ   r(   r)   r;   s   @r
   rS   rS   J   sU   ø‡ € ñ`ð +=÷ 5ò 5÷ð ÷(ð (ð'Ø^b÷'ò '÷Rð ÷4ö r   rS   )r'   rC   ÚloggingrF   Útypingr   r   Ú	getLoggerr#   rz   rŽ   ry   r   r~   r   r,   r/   rS   r?   r   r
   Ú<module>r’      sp   ðñ	ó Û Û ß !à	×	Ò	˜8Ó	$€ð Ð ð Ð õKôY˜Lô Y÷Lñ L÷$]ñ ]÷ió ir   