+
    jHàj/4  ã                  ó¶   € R t ^ RIHt ^ RIt^ RIt^ RIt^ RIt^ RIHt ^ RI	H
t
HtHt ^ RIHtHtHtHt ]P$                  ! R4      tRtR R	 lt ! R
 R4      tR# )a   Session housekeeping / stall / catalog-refresh watcher loops, bound onto ``GatewayRunner`` via the MRO.

``gateway.run`` internals are imported lazily inside method bodies (import cycle), so
``patch("gateway.run.X")`` keeps intercepting them at call time.
)ÚannotationsN)ÚCounter)ÚAnyÚDictÚOptional)Ú!format_session_stall_notificationÚ*resolve_session_idle_seconds_from_activityÚ'should_clear_session_stall_notificationÚ&should_emit_session_stall_notificationzgateway.rung      ¬@c               ó    € V ^8„  d   QhRRRR/# )é   ÚsecondsÚintÚreturnÚNone© )Úformats   "Ú9/home/ubuntu/.hermes/hermes-agent/gateway/run_watchers.pyÚ__annotate__r      s   € ÷ ñ °ð ¸ñ ó    c              ƒ  óœ   "  € \        V4       F6  pV P                  '       g    R# \        P                  ! ^4      G Rj  x€L
  K8  	  R#  L
5i)zSSleep in 1s increments so the watcher stops quickly when ``runner._running`` flips.N)ÚrangeÚ_runningÚasyncioÚsleep)Úrunnerr   Ú_s   && r   Ú_interruptible_sleepr      s6   é € ä�7Ž^ˆØ��ˆÚÜ�mŠm˜AÓ×Òó ñ 	ùs   ‚=A¿A
Á Ac                  óŽ   € ] tR t^%tRtRR R lltR R ltR R ltR R	 ltR
 R lt	R R lt
R R ltR R ltRR R lltRtR# )ÚGatewaySessionWatchersMixinzOSession housekeeping / stall / catalog-refresh watcher loops for GatewayRunner.c               ó   € V ^8„  d   QhRR/# )r   Úintervalr   r   )r   s   "r   r   Ú(GatewaySessionWatchersMixin.__annotate__(   s   € ÷ 7ñ 7¸Cñ 7r   c              ƒ  ó2  "  € \         P                  ! ^<4      G Rj  x€L
  V P                  '       d/    V P                  4       G Rj  x€L
  \        W4      G Rj  x€L
  K@  R#  LF L  \         d!   p\
        P                  RT4        Rp?LARp?ii ; i L:5i)z7Reclaim resources without ending durable conversations.NzSession housekeeping error: %s)r   r   r   Ú_session_housekeepingÚ	ExceptionÚloggerÚdebugr   )Úselfr!   Úes   && r   Ú_session_housekeeping_watcherÚ9GatewaySessionWatchersMixin._session_housekeeping_watcher(   s~   é € ä�mŠm˜BÓ×ÐØ�m�mˆmðBØ×0Ñ0Ó2×2Ð2ô ' tÓ6×6Ò6ñ ñ 	 ñ 3øÜô BÜ—‘Ð=¸q×AÑAûðBúá6ùsV   ‚BœA#�B´A' ÁA%ÁA' ÁBÁBÁ	BÁ%A' Á'BÁ2BÂBÂBÂBc               ó   € V ^8„  d   QhRR/# ©r   r   r   r   )r   s   "r   r   r"   2   s   € ÷ <ñ <¨Tñ <r   c              ƒ  ó*  "  €  V P                  4       ;p'       d   \        P                  RV4        T P                  4        \        T RR4      p\        P                  ! 4       T,
          \        8”  d�    \        \        T P                  R^ 4      ;'       g    ^ 4      pT^ 8”  dB   T P                  P                  T4      G Rj  x€L
 ;p'       d   \        P                  RT4       \        P                  ! 4       T n        R# R#   \         d!   p\        P	                  RT4        Rp?LúRp?ii ; i  \         d"   p\        P	                  RT4        Rp?ELRp?ii ; i L�  \         d!   p\        P	                  R	T4        Rp?L£Rp?ii ; i5i)
zDIdle/pressure agent-cache sweeps plus the hourly SessionStore prune.z+Agent cache idle sweep: evicted %d agent(s)zIdle agent sweep failed: %sNz%Agent cache pressure sweep failed: %sÚ_last_session_store_prune_tsç        Úsession_store_max_age_daysz,SessionStore prune: dropped %d stale entrieszSessionStore prune failed: %s)Ú_sweep_idle_cached_agentsr&   Úinfor%   r'   Ú!_sweep_agent_cache_under_pressureÚgetattrÚtimeÚ_SESSION_STORE_PRUNE_INTERVALr   ÚconfigÚasync_session_storeÚprune_old_entriesr/   )r(   Úevictedr)   Úprune_tsÚmax_ageÚns   &     r   r$   Ú1GatewaySessionWatchersMixin._session_housekeeping2   sL  é € ð	;Ø×8Ñ8Ó:Ð:ˆwÖ:Ü—‘ÐIÈ7ÔSð	Eð
 ×2Ñ2Ô4ô ˜4Ð!?ÀÓEˆÜ�9Š9‹;˜Õ!Ô$AÔAðAÜœg d§k¡kÐ3OÐQRÓS×XÐXÐWXÓY�Ø˜Q”;¨t×/GÑ/G×/YÑ/YÐZaÓ/b×)bÐ$b AÖ$bÜ—K‘KÐ NÐPQÔRô 15·	²	³ˆDÖ-ñ Bøô ô 	;Ü�L‰LÐ6¸×:Ñ:ûð	;ûô ô 	EÜ�L‰LÐ@À!×DÒDûð	Eúñ *cøäô AÜ—‘Ð<¸a×@Ñ@ûðAüs™   ‚F„.D ³D4 Á2FÁ6"E% Â,E% ÃE#ÃE% ÃE% Ã(FÄD1ÄD,Ä'FÄ,D1Ä1FÄ4E Ä?EÅFÅE Å FÅ#E% Å%FÅ0FÆFÆFÆFc               ó   € V ^8„  d   QhRR/# )r   r   Úfloatr   )r   s   "r   r   r"   M   s   € ÷ ?ñ ?°ñ ?r   c                ó    € ^ RI Hp V! RR4      # )zCReturn configured stall timeout (seconds); 0 disables the watchdog.)Ú
_float_envÚHERMES_SESSION_STALL_TIMEOUTé,  )Úgateway.runrC   )r(   rC   s   & r   Ú_session_stall_timeout_secondsÚ:GatewaySessionWatchersMixin._session_stall_timeout_secondsM   s   € å*ÙÐ8¸#Ó>Ð>r   c               ó    € V ^8„  d   QhRRRR/# )r   Úsession_keyÚstrr   zOptional[dict]r   )r   s   "r   r   r"   R   s   € ÷ >ñ >°sð >¸~ñ >r   c                óê   € ^ RI Hp \        V RR4      ;'       g    / P                  V4      pVe   W2J d   R#  VP	                  4       p\        T\        4      '       d   T# R#   \
         d     R# i ; i)zdStall-progress snapshot from ``AIAgent.get_activity_summary()`` only; no other clocks.

See #72039.
)Ú_AGENT_PENDING_SENTINELÚ_running_agentsN)rF   rM   r5   ÚgetÚget_activity_summaryr%   Ú
isinstanceÚdict)r(   rJ   rM   ÚagentÚsummarys   &&   r   Ú_session_activity_for_stallÚ7GatewaySessionWatchersMixin._session_activity_for_stallR   sv   € õ
 	8Ü˜Ð0°$Ó7×=Ð=¸2×BÑBÀ;ÓOˆØŠ=˜EÓ<Ùð	Ø×0Ñ0Ó2ˆGô % W¬d×3Ò3ˆwÐ=¸Ð=øô ô 	Úð	ús   ¹A# Á#A2Á1A2c               ó   € V ^8„  d   QhRR/# )r   r   zdict[str, tuple[Any, Any]]r   )r   s   "r   r   r"   a   s   € ÷ ñ Ð#=ñ r   c                óà  € / p\        V R/ 4      .\        V R/ 4      P                  4       O5pV UUu/ uF4  p\        VP                  4       4       F  qDf   K  \        V4      VbK  	  K6  	  pppVP                  4        FY  p\        VRR4      ;'       g    / p\        VP	                  4       4       F"  w  r‰V'       g   K  W�9  g   K  V	f   K  Wi3W&   K$  	  K[  	  \        \        V RR4      ;'       g    / P	                  4       4       FZ  w  rŠV'       d   W�9   g	   V
'       g   K  \        V
^ ,          RR4      pVf   K7  V P                  V4      ;pf   KN  Wj^ ,          3W&   K\  	  V# u uppi )z­session_key -> (adapter, pending event) from every live adapter's pending slot (default
+ multiplex profiles, deduped by identity), then the overflow queues; first one wins.ÚadaptersÚ_profile_adaptersNÚ_pending_messagesÚ_queued_eventsÚsource)r5   ÚvaluesÚlistÚidÚitemsÚ_delivery_adapter_for)r(   Ú
candidatesÚmapsÚmÚarY   ÚadapterÚpendingrJ   ÚeventÚoverflowr]   s   &           r   Ú_stall_candidatesÚ-GatewaySessionWatchersMixin._stall_candidatesa   sG  € ð 24ˆ
Ü˜˜j¨"Ó-Ð`´¸Ð>QÐSUÓ0V×0]Ñ0]Ó0_Ñ`ˆÙ&*ÔV¡d ´D¸¿¹»Ö4D¨q”H”B�q“E˜1’HÑ4D‘E¡dˆÑVØ—‘Ö(ˆGÜ˜gÐ':¸DÓA×GÐGÀRˆGÜ&*¨7¯=©=«?Ö&;Ñ"�ß‘; ;Ö#@ÀUÔEVØ/6Ð.>�JÓ+ó '<ñ )ô
 &*¬7°4Ð9IÈ4Ó+P×+VÐ+VÐTV×*]Ñ*]Ó*_Ö%`Ñ!ˆKß +Ô";Ç8ÙÜ˜X a�[¨(°DÓ9ˆFØÔ!°$×2LÑ2LÈVÓ2TÐ'T wÔ&aØ+2¸QµKÐ*@�
Ó'ñ &að Ðùó Ws   ²!E*ÁE*c               ó    € V ^8„  d   QhRRRR/# )r   Útimeout_secondsrA   r   r   r   )r   s   "r   r   r"   t   s   € ÷ ñ ¸5ð ÀSñ r   c              ƒ  ó²  "  € \        V RR4      f   / V n        V P                  p^ \        P                  ! 4       V P                  4       rTp\	        VP                  4       4       F³  w  pw  rxV P                  V4      p	\        W”R7      p
\        WRR7      '       d   VP                  VR4       V
e-   \        WR\        VP                  V4      4      R7      '       g   K|  T P                  YgYŠT	;'       g    / W4      G Rj  x€L
 '       g   Kª  V^,          pKµ  	  V Uu. uF  q»V9  g   K  VNK  	  up F  pVP                  VR4       K  	  V#  LNu upi 5i)zQNotify once per stall episode for pending inbound sessions; returns notices sent.Ú_session_stall_notifiedN©ÚnowT)rn   Úidle_secondsÚhas_pending_inbound)rn   rs   rt   Úalready_notified)r5   rp   r6   rk   r_   ra   rU   r   r	   Úpopr
   ÚboolrO   Ú_notify_session_stall)r(   rn   Únotified_mapÚsentrr   rc   rJ   rg   Úpending_eventÚactivityrs   ÚkÚkeys   &&           r   Ú_check_session_stallsÚ1GatewaySessionWatchersMixin._check_session_stallst   sN  é € ä�4Ð2°DÓ9ÒAØ+-ˆDÔ(Ø×3Ñ3ˆØ !¤4§9¢9£;°×0FÑ0FÓ0H�:ˆä59¸*×:JÑ:JÓ:LÖ5MÑ1ˆKÑ1˜'Ø×7Ñ7¸ÓDˆHÜEÀhÔXˆLÜ6Ø /Ð`d÷ó ð × Ñ  ¨dÔ3ØÒ#Ô+QØ /Ø$(¼4À×@PÑ@PÐQ\Ó@]Ó;^÷,ó ,ñ Ø×/Ñ/Ø mÀ8Ç>À>ÈrØó÷ ÷ ð ð ˜•	’ñ! 6Nñ$  ,ÓC™|˜!¸
Ñ/B—A�A™|ÔCˆCØ×Ñ˜S $Ö'ñ Dàˆñùò Dùs6   ‚C3EÃ6EÄEÄEÄEÄEÄ,EÄ2EÅEc               ó0   € V ^8„  d   QhRRRRRRRRRRR	R
/# )r   rJ   rK   rs   rA   r|   rR   rn   ry   r   rw   r   )r   s   "r   r   r"   ‘   sC   € ÷ Cñ C°sð CØ27ðCØCGðCØZ_ðCà26ðCà;?ñCr   c              ƒ  óÜ  aaaaaa"  € ^ RI Ho \        P                  RYT\	        ^\        V^<,          4      4      VP                  R4      ;'       g    VP                  R4      ;'       g    RVP                  R4      ;'       g    VP                  R4      ;'       g    R4       \        VRR	4      o\        SR
R	4      '       g   \        P                  RV4       RWq&   R# \        SRR	4      ;'       g    / P                  V4      R	J;'       g0    \        \        V RR	4      ;'       g    / P                  V4      4      p\        V P                  V4      \        P                  ! 4       R7      p	V'       d   V	e2   W–8  d,   \        P                  RWV	4       VP                  VR	4       R# ^ RIHp
 ^ RI Hp  V P#                  S4      o\%        V4      oR	oVVVVVV3R lpV! V P'                  S4      4      ;_uu_4       GR	j  x€L
  V
! VSP(                  R7      G R	j  x€L
 pR	R	R	4      GR	j  x€L
  X'       g   RWq&   R# Se(   \        SRR4      RJ d   \+        \        SRR4      4      hRYq&   R#  Lo LU LG  + GR	j  x€L 
 '       g   i     L^; i  \,         d    \        P                  RST4        R# \.         d"   p\        P                  RY4        R	p?R# R	p?ii ; i5i)z›Log one stall episode and deliver the notice. True only when sent (latched);
undeliverable (no chat_id) latches without sending; send failures never latch.)Ú"_STALL_NOTIFY_SEND_TIMEOUT_SECONDSz Session stall detected: session=%s idle=%.0fs (timeout=%.0fs, ~%d min); pending inbound present | last_activity=%s | provenance=%s (agent.session_stall_timeout)Úlast_activity_descÚlast_activity_descriptionÚunknownÚ
provenanceÚlast_activity_provenancer]   NÚchat_idz5Session stall notify skipped (no chat_id): session=%sTFr[   r\   rq   zSSession stall notify aborted (no longer stale): session=%s pending=%s fresh_idle=%s)Úpresent_notification)Ú_async_profile_runtime_scopec               “  óž   <"  € \         P                  ! SP                  \        SP                  4      SSR 7      S R7      G Rj  x€L
 oR#  L5i))Úmetadata)ÚtimeoutN)r   Úwait_forÚsendrK   r‰   )rƒ   rg   r�   ÚnoticeÚresultr]   s   €€€€€€r   Úsend_noticeÚFGatewaySessionWatchersMixin._notify_session_stall.<locals>.send_noticeº   s@   øé € ô  '×/Ò/Ø—L‘L¤ V§^¡^Ó!4°fÀx�LÓPØ>ô ÷ ’ñ ùs   ƒAAÁAÁA)ÚplatformÚsuccessÚerrorzsend returned success=FalsezLSession stall notify send timed out after %.0fs for %s; will retry next tickz&Session stall notify failed for %s: %s)rF   rƒ   r&   ÚwarningÚmaxr   rO   r5   rw   r   rU   r6   r3   rv   Úgateway.warning_notificationsrŠ   r‹   Ú_thread_metadata_for_sourcer   Ú _resolve_profile_home_for_sourcer•   ÚRuntimeErrorÚTimeoutErrorr%   )r(   rJ   rg   r{   rs   r|   rn   ry   Ústill_pendingÚ
fresh_idlerŠ   r‹   r“   Ú	presentedÚexcrƒ   r�   r‘   r’   r]   s   &&f&&&&&       @@@@@r   rx   Ú1GatewaySessionWatchersMixin._notify_session_stall‘   s�  ýé € õ
 	CÜ�‰ð_à ¼¸A¼sÀ<ÐSUÕCUÓ?VÓ8WØ�L‰LÐ-Ó.÷ ð °(·,±,Ð?ZÓ2[÷ ð ØØ�L‰L˜Ó&×_Ð_¨(¯,©,Ð7QÓ*R×_Ð_ÐV_ô	
ô ˜¨°$Ó7ˆÜ�v˜y¨$×/Ò/Ü�N‰NÐRÐT_Ô`Ø(,ˆLÑ%Ùô �WÐ1°4Ó8×>Ð>¸B×CÑCÀKÓPÐX\Ð\÷ Tð TÜ”W˜TÐ#3°TÓ:×@Ð@¸b×EÑEÀkÓRÓSð 	ô @Ø×,Ñ,¨[Ó9¼t¿yºy»{ô
ˆ
÷  Ò!7¸JÔ<XÜ�K‰Kð (Ø)4ÀZôQà×Ñ˜[¨$Ô/ÙÝFÝ<ð	Ø×7Ñ7¸Ó?ˆHÜ6°|ÓDˆFØˆF÷ò ñ 4°D×4YÑ4YÐZ`Ó4a×b×bÕbÙ"6°{ÈVÏ_É_Ô"]×]�	÷ c×bçØ,0�Ñ)ÙàÒ!¤g¨f°iÀÓ&FÈ%Ó&OÜ"¤7¨6°7Ð<YÓ#ZÓ[Ð[ð %)ˆÑ!Ùñ% cÙ]÷ c×b×bÒbûô ô 	Ü�N‰NØ^Ø2°Kôò Üô 	Ü�N‰NÐCÀ[ÔVÝûð	üsÛ   ˆA$K,Á-K,ÂK,Â AK,Ã7K,ÄK,Ä,BK,Æ;AJ ÈI7ÈJ ÈI=È#I9È$I=È(J È3I;È4J É J ÉK,É+J É1K,É7J É9I=É;J É=J	ÊJÊ
J	ÊJ	ÊJ Ê"K)Ê;K,Ê>K)ËK)ËK$ËK,Ë$K)Ë)K,c               ó   € V ^8„  d   QhRR/# r-   r   )r   s   "r   r   r"   Ö   s   € ÷ Vñ V°dñ Vr   c              ƒ  óŽ  "  € ^ RI HpHp \        P                  ! ^4      G Rj  x€L
  V P
                  '       dÂ    \        P                  ! V4      G Rj  x€L
   V! 4       p\        P                  ! 4       V,           pV P
                  '       g   Ki  \        P                  ! 4       V8  g   K…  \        P                  ! \        R\        RV\        P                  ! 4       ,
          4      4      4      G Rj  x€L
  K~  R#  LÙ Lª  \         d!   p\        P                  RT4        Rp?LÎRp?ii ; i  \         d    Rp LÝi ; i LM5i)z«Refresh the /model picker's remote catalogs every TTL window. The picker itself only
refreshes on a cold/stale open, so if nobody opens ``/model`` the cache never updates.)Úrefresh_catalogsÚrefresh_interval_secondsNz Model catalog refresh failed: %sg     À’@ç      >@r0   )Úhermes_cli.model_catalogr¦   r§   r   r   r   Ú	to_threadr%   r&   r'   r6   Ú	monotonicÚminr™   )r(   r¦   r§   r¢   r!   Údeadlines   &     r   Ú_model_catalog_refresh_watcherÚ:GatewaySessionWatchersMixin._model_catalog_refresh_watcherÖ   sò   é € ÷ 	XÜ�mŠm˜BÓ×ÐØ�m�mˆmðFÜ×'Ò'Ð(8Ó9×9Ð9ð"Ù3Ó5�ô —~’~Ó'¨(Õ2ˆHØ—-—-’-¤D§N¢NÓ$4°xÖ$?Ü—m’m¤C¨¬c°#°xÄ$Ç.Â.ÓBRÕ7RÓ.SÓ$TÓU×UÒUñ ñ 	 ñ :øÜô FÜ—‘Ð?À×EÑEûðFûô ô "Ø!’ð"úñ Vùs�   ‚"E¤C>¥E¼D ÁD ÁD ÁD0 Á"+EÂEÂ.AEÃ5EÃ6	EÄ D ÄD-ÄD(Ä#EÄ(D-Ä-EÄ0E Ä=EÄ?E Å Ec               ó   € V ^8„  d   QhRR/# )r   r!   rA   r   )r   s   "r   r   r"   è   s   € ÷ Kñ K°Uñ Kr   c              ƒ  óÖ  "  € \         P                  ! \        R\        R\	        V4      4      4      4      G Rj  x€L
  V P
                  '       dd    V P                  4       ;p^ 8”  d   V P                  V4      G Rj  x€L
  \        T \        ^\        \	        T4      4      4      4      G Rj  x€L
  Ku  R#  L{ L:  \         d!   p\        P                  RT4        Rp?L^Rp?ii ; i L:5i)a  Pending-inbound + stale-activity stall watchdog. Progress comes only from
``get_activity_summary()``; pending inbound is a notify policy gate, not a progress clock.
Notify-only: never kills the turn (contrast ``gateway_timeout`` / ``shutdown_watchdog``).

See #72016.
See #72039.
r¨   g      ð?NzSession stall watcher error: %s)r   r   r¬   r™   rA   r   rG   r   r%   r&   r'   r   r   )r(   r!   rŽ   r¢   s   &&  r   Ú_session_stall_watcherÚ2GatewaySessionWatchersMixin._session_stall_watcherè   s½   é € ô �mŠmœC ¤c¨#¬u°X«Ó&?Ó@ÓA×AÐAØ�m�mˆmðEØ#×BÑBÓDÐD�GÈÔIØ×4Ñ4°WÓ=×=Ð=ô ' t¬S°´C¼¸h»Ó4HÓ-IÓJ×JÒJñ ñ 	Bñ >øÜô EÜ—‘Ð>À×DÑDûðEúáJùsW   ‚7C)¹B5ºC)Á+B9 Á<B7Á=B9 Â+C)Â,C'Â-	C)Â7B9 Â9C$ÃCÃC)ÃC$Ã$C))r/   rp   N)rE   )r¨   )Ú__name__Ú
__module__Ú__qualname__Ú__firstlineno__Ú__doc__r*   r$   rG   rU   rk   r   rx   r®   r²   Ú__static_attributes__r   r   r   r   r   %   s<   † ÙY÷7õ<õ6?õ
>õõ&õ:CõJV÷$Kó Kr   r   )r¸   Ú
__future__r   r   Ú
contextlibÚloggingr6   Úcollectionsr   Útypingr   r   r   Úgateway.session_stallr   r   r	   r
   Ú	getLoggerr&   r7   r   r   r   r   r   Ú<module>rÁ      sY   ðñõ #ã Û Û Û Ý ß &Ñ &÷ó ð 
×	Ò	˜=Ó	)€à &Ð õ÷SKó SKr   