+
    jHàj3)  ã                  ó2  € R t ^ RIHt ^ RIt^ RIt^ RIt^ RIt^ RIt^ RIH	t	 ]P                  ! R4      tRtRtRtR!tR R	 ltR
 R lt]P$                  R 4       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 ltR R ltR R  ltR# )"a^  Idle exit for an auto-started (unmanaged) gateway.

A client that finds no gateway for its home starts a detached ``gateway run --idle-exit``
(``hermes_cli.gateway_runtime_start``). Nothing else ever stops that process, so every throwaway home
(scripts, tests, one-shot ``chat -q``) used to leave a ~270 MB daemon behind. This watcher ends it once
it has served nobody and has nothing to do for ``gateway.unmanaged_idle_exit_seconds`` (default 600,
``0`` disables):

- no attached client (live ticketed WebSocket, outstanding ticket, or recent HTTP/WS activity);
- no messaging adapter configured, connected or reconnecting (any profile);
- no runnable cron job and no kanban task the dispatcher must act on;
- no queued/started admission, live managed worker, in-flight agent/cron/API run, background
  process or async delegation, active heartbeat/loop, pending handoff or hosted-room task.

Every probe fails CLOSED: an unreadable source counts as work, so the gateway stays up. A gateway with
a supervisor (systemd/launchd/Desktop/external) or started without ``--idle-exit`` (installed services,
``hermes gateway run`` by hand, ``hermes gateway ensure`` from Desktop) never arms this. The exit
publishes ``draining`` with ``drain_reason: idle_exit`` before anything else, so a client that races it
waits for the process to go and starts a fresh one instead of failing.
)ÚannotationsN©ÚPathzgateway.rung     À‚@Ú	idle_exitzauto-started gateway idlec               ó   € V ^8„  d   QhRR/# )é   ÚreturnÚfloat© )Úformats   "Ú:/home/ubuntu/.hermes/hermes-agent/gateway/run_idle_exit.pyÚ__annotate__r   )   s   € ÷ ñ  ñ ó    c                óT  € ^ RI Hp V! 4       ;'       g    / P                  R4      p\        V\        4      '       d   VP                  R4      MRpVf   \
        #  \        V4      p\        RT4      #   \        \        3 d%    \        P                  RT\
        4       \
        u # i ; i)za``gateway.unmanaged_idle_exit_seconds`` from the launch home; 0 disables, junk keeps the default.)Ú_load_gateway_configÚgatewayÚunmanaged_idle_exit_secondsNzBgateway.unmanaged_idle_exit_seconds=%r is not a number; using %.0fç        )Úgateway.runr   ÚgetÚ
isinstanceÚdictÚDEFAULT_IDLE_EXIT_SECONDSr	   Ú	TypeErrorÚ
ValueErrorÚloggerÚwarningÚmax)Úrunnerr   ÚsectionÚvalueÚsecondss   &    r   Úidle_exit_secondsr"   )   s—   € å0Ù#Ó%×+Ð+¨×0Ñ0°Ó;€GÜ:DÀWÌd×:SÒ:SˆG�K‰KÐ5Ô6ÐY]€EØ‚}Ü(Ð(ð)Ü˜“,ˆô
 ˆs�GÓÐøô	 ”zÐ"ô )Ü�‰Ð[Ð]bÜ0ô	2ä(Ò(ð)ús   ÁA2 Á22B'Â&B'c               ó   € V ^8„  d   QhRR/# ©r   r   ÚNoner
   )r   s   "r   r   r   9   s   € ÷ 7ñ 7 Dñ 7r   c                ó:   € \         P                  ! 4       V n        R # ©N)ÚtimeÚ	monotonicÚ_idle_exit_last_activity©r   s   &r   Únote_client_activityr,   9   s   € Ü&*§n¢nÓ&6€FÖ#r   c              #  óú   "  € \        V 4       \        V R^ 4      ^,           V n         Rx € V ;P                  ^,          un        \        V 4       R#   T ;P                  ^,          un        \        T 4       i ; i5i)zVCount one live client connection for the idle check (and stamp activity on both ends).Ú_idle_exit_clientsN)r,   Úgetattrr.   r+   s   &r   Úattached_clientr0   =   sc   é € ô ˜Ô Ü '¨Ð0DÀaÓ HÈ1Õ L€FÔð%Ûà×!Ò! QÕ&Õ!Ü˜VÖ$øð 	×!Ò! QÕ&Õ!Ü˜VÕ$üs   ‚%A;¨A ¬&A;Á&A8Á8A;c               ó   € V ^8„  d   QhRR/# ©r   r   ú
str | Noner
   )r   s   "r   r   r   I   s   € ÷ 9ñ 9˜zñ 9r   c           
     óÌ  € ^ RI Hp \        \        V RR4      ;'       g    / 4      p\        V RR4      ;'       g    / P	                  4        F  pVP                  V4       K  	  \        V RR4      ;'       g    / P	                  4        F  pTP                  T;'       g    / 4       K   	  TP                  \        V RR4      ;'       g    / 4       \        V RR4      pVe1   VP                  R VP                  P                  4        4       4       V Uu0 uF+  qfVP                  8w  g   K  \        VR	\        V4      4      kK-  	  ppV'       d   R
\        V4       2# R# u upi )é    )ÚPlatformÚadaptersNÚ_profile_adaptersÚ_profile_failed_platformsÚ_failed_platformsÚconfigc              3  óT   "  € T F  w  r\        VR R4      '       g   K  Vx € K   	  R# 5i)ÚenabledFN)r/   )Ú.0ÚpÚcfgs   &  r   Ú	<genexpr>Ú"_messaging_busy.<locals>.<genexpr>S   s"   é € Ð_Ñ$<™&˜!ÄÈÈYÐX]×@^—A’AÓ$<ùs   ‚(ž
(r    z
messaging )Úgateway.configr6   Úlistr/   ÚvaluesÚextendÚ	platformsÚitemsÚLOCALÚstrÚsorted)r   r6   Úliver7   Úpendingr;   r?   s   &      r   Ú_messaging_busyrN   I   s(  € Ý'Ü”˜ 
¨DÓ1×7Ð7°RÓ8€DÜ˜VÐ%8¸$Ó?×EÐEÀ2×MÑMÖOˆØ�‰�HÖñ Pä˜FÐ$?ÀÓF×LÐLÈ"×TÑTÖVˆØ�‰�G—M�M˜rÖ"ñ Wà‡K�K”˜Ð 3°TÓ:×@Ð@¸bÔAÜ�V˜X tÓ,€FØÒØ�‰Ñ_ F×$4Ñ$4×$:Ñ$:Ô$<Ó_Ô_Ù15ÓM±¨A¸h¿n¹nÑ9LÔ'ŒG�A�w¤ A£Ö'±€DÐMß*.ˆZœ˜t›�~Ð&Ð8°DÐ8ùò Ns   ÄE!Ä-E!c               ó   € V ^8„  d   QhRR/# )r   r   z
list[Path]r
   )r   s   "r   r   r   X   s   € ÷ ñ ˜Zñ r   c                óÞ   € ^ RI Hp ^ RIHp V! V 4       Uu0 uF  p\	        VP
                  4      kK  	  ppVP                  R V! V P                  4       4       4       \        V4      # u upi )r5   )Ú_cron_tick_profile_homes©Úall_authoritiesc              3  ó<   "  € T F  w  r\        V4      x € K  	  R # 5ir'   r   )r>   Ú_nameÚhs   &  r   rA   Ú _served_homes.<locals>.<genexpr>\   s   é € ÐQÑ)P™X˜U”�a—�Ó)Pùs   ‚)	r   rQ   Úgateway.session_authoritiesrS   r   Ú
profile_idÚupdater;   rK   )r   rQ   rS   ÚaÚhomess   &    r   Ú_served_homesr]   X   sX   € Ý4Ý;Ù)8¸Ô)@ÓAÑ)@ AŒT�!—,‘,ÖÑ)@€EÐAØ	‡L�LÑQÑ)AÀ&Ç-Á-Ô)PÓQÔQÜ�%‹=Ðùò Bs   —A*c               ó   € V ^8„  d   QhRR/# r2   r
   )r   s   "r   r   r   `   s   € ÷ ñ ˜ñ r   c                ó¢  aa€ ^ RI HoHoHpHp ^ RIHp V! 4       '       d   R# V  F’  pVR,          R,          P                  4       '       g   K)  V! V4      ;_uu_ 4        V! 4       pRRR4       \        ;QJ d     VV3R lX 4       F  '       g   K   RM	  R	M! VV3R lX 4       4      '       g   K�  R
V 2u # 	  R#   + '       g   i     L_; i)r5   )Úis_job_runnableÚis_terminal_jobÚ	load_jobsÚuse_cron_store)Úget_running_job_idszcron job runningÚcronz	jobs.jsonNc              3  ó^   <"  € T F"  pS! V4      ;'       d    S! V4      '       * x € K$  	  R # 5ir'   r
   )r>   Újobr`   ra   s   & €€r   rA   Ú_cron_busy.<locals>.<genexpr>j   s)   øé € ÐQÉDÀS‰˜sÓ#×@Ð@©O¸CÓ,@Ô(@Ô@ËDùs   ƒ-—-TFzcron jobs scheduled in )	Ú	cron.jobsr`   ra   rb   rc   Úcron.schedulerrd   ÚexistsÚany)r\   rb   rc   rd   ÚhomeÚjobsr`   ra   s   &     @@r   Ú
_cron_busyro   `   s�   ù€ ßUÓUÝ2Ù×ÒÙ!ÛˆØ�v• Õ+×3Ñ3×5Ò5ÙÙ˜D×!Õ!Ù“;ˆD÷ "ç‹3ÕQÉDÓQ�3�3Š3ÕQÉDÓQ×QÔQØ,¨T¨FÐ3Ò3ñ ñ ÷	 "×!ús   ÁB>Â>Cc               ó   € V ^8„  d   QhRR/# r2   r
   )r   s   "r   r   r   o   s   € ÷ ñ �jñ r   c            	     óJ  € ^ RI Hp  RP                  R\        \        4      ,          4      pV P                  RR7       FÍ  pV P                  VP                  R4      R7      pVP                  4       '       g   K<  \        P                  ! \        P                  ! RV R	2R
^R7      4      ;_uu_ 4       pVP                  RV R2\        4      P                  4       '       d!   RVP                  R4       2uuRRR4       u #  RRR4       KÏ  	  R#   + '       g   i     Kä  ; i)r5   )Ú	kanban_dbÚ,Ú?F)Úinclude_archivedÚslug)Úboardzfile:z?mode=roT)ÚuriÚtimeoutz%SELECT 1 FROM tasks WHERE status IN (z	) LIMIT 1zkanban work on board N)Ú
hermes_clirr   ÚjoinÚlenÚ_KANBAN_DUE_STATUSESÚlist_boardsÚkanban_db_pathr   rk   Ú
contextlibÚclosingÚsqlite3ÚconnectÚexecuteÚfetchone)rr   ÚmarksÚmetaÚpathÚconns        r   Ú_kanban_busyrŠ   o   sï   € Ý$Ø�H‰H�Sœ3Ô3Ó4Õ4Ó5€EØ×%Ñ%°uÐ%Ö=ˆØ×'Ñ'¨d¯h©h°vÓ.>Ð'Ó?ˆØ�{‰{�}Š}ÙÜ×Ò¤§¢°%¸°v¸XÐ0FÈDÐZ[Ô \×]Ô]ÐaeØ�|‰|ÐCÀEÀ7È)ÐTÜ0ó2ß2:±(³*ö=à.¨t¯x©x¸Ó/?Ð.@ÐA÷ ^Ô]ð=÷ ^Ñ]ñ	 >ñ ÷	 ^×]Ð]ús   Â3ADÄD"c               ó   € V ^8„  d   QhRR/# r2   r
   )r   s   "r   r   r   }   s   € ÷ ñ ˜Jñ r   c                ó¬  € ^ RI Hp V! V 4       FÁ  pVP                  pVP                  R4      '       d   RVP                   2u # VP                  R4      '       d   RVP                   2u # \        VRR4      pVf   Kr  VP                  4       pVP                  R4      '       g   VP                  R	4      '       g   K²  R
VP                   2u # 	  R# )r5   rR   zMSELECT 1 FROM session_admissions WHERE status IN ('queued','started') LIMIT 1zadmissions pending in zBSELECT 1 FROM worker_executions WHERE status != 'terminal' LIMIT 1zmanaged worker live in Úhosted_room_serviceNÚcurrent_tasksÚleased_roomszhosted-room work in )rX   rS   ÚdbÚ	_read_allrY   r/   Ústatusr   )r   rS   Ú	authorityr�   Úservicer’   s   &     r   Ú_ledger_busyr•   }   s¼   € Ý;Ù$ VÖ,ˆ	Ø�\‰\ˆØ�<‰<Ðg×hÒhØ+¨I×,@Ñ,@Ð+AÐBÒBØ�<‰<Ð\×]Ò]Ø,¨Y×-AÑ-AÐ,BÐCÒCÜ˜)Ð%:¸DÓAˆØÔØ—^‘^Ó%ˆFØ�z‰z˜/×*Ò*¨f¯j©j¸×.HÔ.HØ-¨i×.BÑ.BÐ-CÐDÒDñ -ñ r   c               ó   € V ^8„  d   QhRR/# r2   r
   )r   s   "r   r   r   �   s   € ÷ ñ  
ñ r   c                óˆ   € ^ RI HpHpHp V  F1  pRV3RV3RV33 F  w  rVV! V4      '       g   K  RV RV 2u u # 	  K3  	  R# )r5   )Úprofile_has_active_heartbeatÚprofile_has_active_loopÚprofile_has_pending_handoffÚ	heartbeatÚloopÚhandoffzactive z in N)Úgateway.run_idle_gatesr˜   r™   rš   )r\   r˜   r™   rš   rm   ÚlabelÚgates   &      r   Ú_store_gates_busyr¡   �   sc   € ÷\ñ \ãˆØ(Ð*FÐGÈ&ÐRiÐIjØ&Ð(CÐDóF‰KˆEá�D�zŒzØ    t¨D¨6Ð2Ô2óFñ ñ
 r   c               ó    € V ^8„  d   QhRRRR/# )r   Úwindowr	   r   r3   r
   )r   s   "r   r   r   ˜   s   € ÷ ;ñ ; 5ð ;¨Zñ ;r   c               óð  €  \        V R^ 4      pV'       d   V R2# \        V RR4      pVe   VP                  4       '       d   R# \        P                  ! 4       \        V RR4      ,
          pWA8  d   RVR	 R
2# V P                  '       g   V P
                  '       g   R# V P                  4       '       d   R# V P                  4       '       d   R# \        V 4      p\        V 4      ;'       gD    \        V 4      ;'       g1    \        V4      ;'       g    \        4       ;'       g    \        V4      #   \         d;   p\        P!                  RRR7       R\#        T4      P$                   R2u Rp?# Rp?ii ; i)zRWhy the gateway must stay up, or None when it may exit. Fails closed on any error.r.   z client(s) attachedÚsession_ticket_storeNzclient attachingr*   r   zclient activity ú.0fzs agozalready stoppingzagent work in flightzbackground work in flightz"idle-exit probe failed; staying upT)Úexc_infozidle probe failed (Ú))r/   Úoutstandingr(   r)   Ú	_drainingÚ_runningÚ_active_work_countÚ'_scale_to_zero_has_live_background_workr]   rN   r•   ro   rŠ   r¡   Ú	Exceptionr   ÚdebugÚtypeÚ__name__)r   r£   ÚclientsÚticketsÚquietr\   Úexcs   &$     r   Úbusy_reasonr¶   ˜   sO  € ð;Ü˜&Ð"6¸Ó:ˆßØ�YÐ1Ð2Ð2Ü˜&Ð"8¸$Ó?ˆØÒ 7×#6Ñ#6×#8Ò#8Ù%Ü—’Ó ¤7¨6Ð3MÈsÓ#SÕSˆØŒ>Ø% e¨C [°Ð6Ð6Ø××Ð 6§?§? ?Ù%Ø×$Ñ$×&Ò&Ù)Ø×9Ñ9×;Ò;Ù.Ü˜fÓ%ˆÜ Ó'÷ ,ð ,¬<¸Ó+?÷ ,ð ,Ä:ÈeÓCT÷ ,ð ,ÔXdÓXf÷ ,ð ,Ü$ UÓ+ð	-øäô ;Ü�‰Ð9ÀDˆÔIØ$¤T¨#£Y×%7Ñ%7Ð$8¸Ð:Õ:ûð;ús^   ‚D0 œ&D0 Á3D0 Á9D0 ÂD0 ÂD0 Â7D0 ÃD0 Ã,D0 Ã?D0 ÄD0 Ä$D0 Ä0E5Ä;/E0Å*E5Å0E5c               ó    € V ^8„  d   QhRRRR/# )r   r£   r	   r   r%   r
   )r   s   "r   r   r   ²   s   € ÷ Gñ G Uð G¨tñ Gr   c                ó  € RV n         V P                  P                  R. \        R7       \         RVR R2V n        \        P                  RV P
                  4       \        P                  ! V P                  4       4      V n        R# )	z\Withdraw admission synchronously (no await between the last idle check and here), then stop.TÚdraining)ÚstateÚcapabilitiesÚdrain_reasonz for r¦   z's (gateway.unmanaged_idle_exit_seconds)z3Exiting: %s; the next client starts a fresh gatewayN)rª   Úsession_runtime_descriptorrZ   ÚIDLE_EXIT_REASONÚIDLE_EXIT_STOP_PREFIXÚ_exit_reasonr   ÚinfoÚasyncioÚensure_futureÚstopÚ_idle_exit_stop_task)r   r£   s   &&r   Ú_begin_idle_exitrÆ   ²   sm   € à€FÔØ
×%Ñ%×,Ñ,°:ÈBÔ]mÐ,ÔnÜ2Ð3°5¸À¸ÐDkÐl€FÔÜ
‡K�KÐEÀv×GZÑGZÔ[Ü")×"7Ò"7¸¿¹»Ó"F€FÖr   c               ó   € V ^8„  d   QhRR/# r$   r
   )r   s   "r   r   r   »   s   € ÷ ñ °ñ r   c              ƒ  ó¨  "  € \        V 4      pV^ 8:  d   \        P                  R4       R# \        P                  RV4       \        R\	        RV^,          4      4      pRpV P
                  '       dÚ   V P                  '       gÆ   \        P                  ! V4      G Rj  x€L
  \        V 4      pV^ 8:  d   RpKX  \        P                  ! \        WR7      G Rj  x€L
 pVe   RpK„  T;'       g    \        P                  ! 4       p\        P                  ! 4       V,
          V8  d	   W8”  d   KÌ  \        WR7      e   KÜ  \        W4       R# R# R#  L¯ Lw5i)r5   zOAuto-started gateway idle exit disabled (gateway.unmanaged_idle_exit_seconds=0)Nz‡Auto-started gateway: exits after %.0fs with no clients, adapters, cron/kanban work or admissions (gateway.unmanaged_idle_exit_seconds)g      >@g      à?)r£   )r"   r   rÁ   Úminr   r«   rª   rÂ   ÚsleepÚ	to_threadr¶   r(   r)   rÆ   )r   r£   ÚintervalÚ
idle_sinceÚreasons   &    r   Úunmanaged_idle_exit_watcherrÏ   »   s  é € Ü˜vÓ&€FØ�„{Ü�‰ÐeÔfÙÜ
‡K�Kð CØDJôLä�4œ˜S &¨1¥*Ó-Ó.€HØ€JØ
�/�/ˆ/ &×"2×"2Ð"2Ü�mŠm˜HÓ%×%Ð%Ü" 6Ó*ˆØ�QŒ;ØˆJÙô ×(Ò(¬°fÔL×LˆØÒØˆJÙØ×3Ð3¤4§>¢>Ó#3ˆ
Ü�>Š>Ó˜jÕ(¨8Ô3¸Ô8IÙÜ�vÔ-Ô5Ü˜VÔ,Ùñ# #3‰/Ù%ñ Mùs=   ‚A0EÁ3EÂEÂEÂ9EÃEÃEÃ.A
EÄ=EÅEc               ó   € V ^8„  d   QhRR/# )r   r   Úboolr
   )r   s   "r   r   r   Ø   s   € ÷ 	ñ 	 tñ 	r   c                ó¢   a € ^ RI Hp V! 4       pVR8w  d   \        P                  RV4       R# \	        S 4       S P                  V 3R lR4       R# )zbStart the watcher for an ``--idle-exit`` launch that no supervisor owns. Returns whether it armed.)Ú_detect_supervisorÚmanualz:Auto-started gateway idle exit not armed: supervised by %sFc                 ó   <€ \        S 4      # r'   )rÏ   r+   s   €r   Ú<lambda>Ú)arm_unmanaged_idle_exit.<locals>.<lambda>à   s   ø€ Ô%@ÀÔ%Hr   rÏ   T)Úgateway.control_socketrÓ   r   rÁ   r,   Ú_spawn_supervised)r   rÓ   Ú
supervisors   f  r   Úarm_unmanaged_idle_exitrÛ   Ø   sF   ø€ å9Ù#Ó%€JØ�XÔÜ�‰ÐPÐR\Ô]ÙÜ˜Ô Ø
×ÑÔHÐJgÔhÙr   )Ú	scheduledÚreadyÚrunningÚreview)Ú__doc__Ú
__future__r   rÂ   r€   Úloggingr‚   r(   Úpathlibr   Ú	getLoggerr   r   r¾   r¿   r}   r"   r,   Úcontextmanagerr0   rN   r]   ro   rŠ   r•   r¡   r¶   rÆ   rÏ   rÛ   r
   r   r   Ú<module>ræ      s¢   ðñõ( #ã Û Û Û Û Ý à	×	Ò	˜=Ó	)€à!Ð ØÐ ð 4Ð àBÐ õõ 7ð ×Ññ%ó ð%õ9õõõõõ õ;õ4Gõ÷:	r   