+
    jHàjòè ã                  óæ  € R t ^ RIHt ^ RIt^ RIt^ RIt^ RIt^ RIt^ RIt^ RI	t	^ RI
t
^ RIt^ RIHtHtHt ^ RIHt ^ RIHt ^ RIHtHtHtHt ^ RIHt ^ RIHt ^ R	IHtHtH t H!t!H"t" ^ R
I#H$t$ ^ RI%H&t&H't'H(t( ^ RI)H*t* ^ RI+H,t,H-t- ]P\                  ! R4      t/R R lt0R R lt1RPe                  4       t3]RR/R R ll4       t4R R lt5R R lt6R R lt7RR/R R  llt8R! R" lt9 ! R# R$]*4      t:R# )%a;  Stop/drain/restart, scale-to-zero and active-work accounting methods for GatewayRunner.

Split out of ``gateway/run.py``; 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)ÚcontextmanagerÚnullcontextÚsuppress)ÚContext)ÚPath)ÚAnyÚCallableÚDictÚOptional)Út)ÚPlatform)Ú"DEFAULT_GATEWAY_CRON_DRAIN_TIMEOUTÚ!GATEWAY_SERVICE_RESTART_EXIT_CODEÚeffective_stop_drain_timeoutÚeffective_stop_watchdog_delayÚresolve_cron_drain_budget)Ú_UNSET)Úinterrupt_side_agentsÚmanaged_turn_countÚstop_managed_turns)ÚGatewaySessionEndMixin)Úarm_shutdown_watchdogÚresolve_shutdown_watchdog_delayzgateway.runc               ó   € V ^8„  d   QhRR/# ©é   ÚreturnÚbool© )Úformats   "Ú9/home/ubuntu/.hermes/hermes-agent/gateway/run_shutdown.pyÚ__annotate__r"   '   s   € ÷ ñ ¨$ñ ó    c                ó’   € V P                   '       g   R# V P                  '       d!   \        P                  RV P                  4       R# )zITrue (after logging the reason) when the runner asked for a failure exit.Fz Gateway exiting with failure: %sT)Úshould_exit_with_failureÚexit_reasonÚloggerÚerror©Úrunners   &r!   Ú_exit_with_failure_verdictr+   '   s5   € à×*×*Ð*ÙØ××ÐÜ�‰Ð7¸×9KÑ9KÔLÙr#   c               ó    € V ^8„  d   QhRRRR/# )r   Úsignal_initiated_shutdownr   r   r   )r    s   "r!   r"   r"   0   s   € ÷ ñ ÀTð Èdñ r#   c                óJ  € \        V 4      '       d   R# V P                  e   \        V P                  4      hV'       d*   V P                  '       g   \        P                  R4       R# V P                  '       d*   \        P                  R\        4       \        \        4      hR# )zJResolve the process verdict after either startup abort or normal shutdown.FzvExiting with code 1 (signal-initiated shutdown without restart request) so the service manager can revive the gateway.z_Exiting with code %d (service-restart requested) so the service manager relaunches the gateway.T)r+   Ú	exit_codeÚ
SystemExitÚ_restart_requestedr'   ÚinfoÚ_restart_via_servicer   )r*   r-   s   &&r!   Ú_resolve_gateway_exit_verdictr4   0   sƒ   € ä! &×)Ò)ÙØ×ÑÒ#Ü˜×)Ñ)Ó*Ð*ß ¨×)B×)BÐ)BÜ�‰ðFô	
ñ à×"×"Ð"Ü�‰ð.ä-ô	
ô
 Ô:Ó;Ð;Ùr#   uI  
import os, subprocess, sys, time
from hermes_cli._subprocess_compat import windows_detach_flags_without_breakaway
pid = int(sys.argv[1])
restart_after_s = float(sys.argv[2])
cmd = sys.argv[3:]
deadline = time.monotonic() + restart_after_s

def _alive(p):
    # On Windows, os.kill(pid, 0) is NOT a no-op â€” it maps to
    # GenerateConsoleCtrlEvent(0, pid) (bpo-14484). Use the
    # Win32 handle-based existence check instead.
    if os.name == 'nt':
        import ctypes
        k32 = ctypes.windll.kernel32
        k32.OpenProcess.restype = ctypes.c_void_p
        k32.WaitForSingleObject.restype = ctypes.c_uint
        k32.GetLastError.restype = ctypes.c_uint
        h = k32.OpenProcess(0x1000 | 0x100000, False, int(p))
        if not h:
            return k32.GetLastError() != 87
        try:
            return k32.WaitForSingleObject(h, 0) == 0x102
        finally:
            k32.CloseHandle(h)
    try:
        os.kill(int(p), 0)
        return True
    except ProcessLookupError:
        return False
    except PermissionError:
        return True
    except OSError:
        return False

while time.monotonic() < deadline:
    if not _alive(pid):
        break
    time.sleep(0.2)
subprocess.Popen(
    cmd,
    stdout=subprocess.DEVNULL,
    stderr=subprocess.DEVNULL,
    creationflags=windows_detach_flags_without_breakaway(),
)
Úexc_infoFc               ó$   € V ^8„  d   QhRRRRRR/# )r   ÚlevelÚintÚmsgÚstrr5   r   r   )r    s   "r!   r"   r"   y   s!   € ÷ /ñ /˜3ð / Sð /¸4ñ /r#   c             '  óø   "  €  Rx € R#   \          dc   pT'       d6   \        P                  ! Y.TO5R\        T4      YDP                  3/   Rp?R# \        P                  ! Y.TOTN5!    Rp?R# Rp?ii ; i5i)aD  ``suppress(Exception)`` that logs the swallowed exception on ``gateway.run``.

Without ``exc_info`` the exception is appended as the last ``%s`` argument (``msg % (*args, exc)``);
with it the traceback is attached instead. Best-effort seams use this everywhere a failure must be
visible in the log but must never propagate.
Nr5   )Ú	Exceptionr'   ÚlogÚtypeÚ__traceback__)r7   r9   r5   ÚargsÚexcs   &&$* r!   Ú_log_suppressedrB   x   s`   é € ð/ÝøÜô /ßÜ�JŠJ�uÐW DÒW´D¸³I¸s×DUÑDUÐ3V×WÐWä�JŠJ�uÐ. DÐ.¨#×.Ò.ûð	/üs6   ‚A:„
 ˆA:ŠA7•7A2ÁA:ÁA2Á,A:Á2A7Á7A:c               ó    € V ^8„  d   QhRRRR/# )r   Úresultr   r   r   r   )r    s   "r!   r"   r"   ‰   s   € ÷ Lñ L˜ð L ñ Lr#   c                ó8   € V RJ;'       d    \        V RR4      RJ # )zBTrue when an adapter ``send()`` result explicitly reports failure.NÚsuccessTF©Úgetattr©rD   s   &r!   Ú_send_failedrJ   ‰   s#   € à˜Ð×KÐK¤'¨&°)¸TÓ"BÀeÐ"KÐKr#   c               ó    € V ^8„  d   QhRRRR/# )r   rD   r   r   r:   r   )r    s   "r!   r"   r"   Ž   s   € ÷ Cñ C˜ð C ñ Cr#   c                ó   € \        V RR4      # )z@Error text of a failed ``send()`` result (adapters may omit it).r(   zsend returned success=FalserG   rI   s   &r!   Ú_send_errorrM   Ž   s   € ä�6˜7Ð$AÓBÐBr#   c               ó    € V ^8„  d   QhRRRR/# )r   Úplatform_valuer:   r   Útupler   )r    s   "r!   r"   r"   “   s   € ÷ Qñ Q sð QÀ5ñ Qr#   c                óF   € T \        V4      V'       d   \        V4      3# R3# )zZDedup key for one notice destination: thread/topic platforms share a chat but route apart.N)r:   )rO   Úchat_idÚ	thread_ids   &&&r!   Ú_notice_target_keyrT   “   s!   € àœC ›L¿I¬#¨i«.ÐPÐPÈ4ÐPÐPr#   Úprofilec               ó$   € V ^8„  d   QhRRRRRR/# )r   rO   r:   rU   úOptional[str]r   rP   r   )r    s   "r!   r"   r"   ˜   s'   € ÷ Bñ B¨ð BÈmð BÐglñ Br#   c               ó|   € ^ RI Hp V'       d$   VR8w  d   V R8X  d   V! V4      '       d   V RV  2p \        WV4      # )aq  Dedupe key for one DELIVERED chat: profile-independent, except Telegram private chats.

Two served profiles can share one home chat (one Telegram group for the whole host) and owe it
ONE notice per host restart. A positive Telegram chat id names the USER, though: the same id
under two bot tokens is two conversations, so those stay keyed per served profile (#118233).
)Ú#looks_like_telegram_private_chat_idÚdefaultÚtelegramÚ:)Úgateway.deliveryrY   rT   )rO   rR   rS   rU   rY   s   &&&$ r!   Ú_delivery_target_keyr^   ˜   sB   € õ Eß�G˜yÔ(¨^¸zÔ-IÙ3°G×<Ò<Ø#˜9 A nÐ%5Ð6ˆÜ˜n°yÓAÐAr#   c               ó    € V ^8„  d   QhRRRR/# )r   r*   Úobjectr   Úfloatr   )r    s   "r!   r"   r"   ¦   s   € ÷ xñ x fð x°ñ xr#   c                ó>   € \        V \        \        V 4      4      4      # )zôThread-watchdog leash for the stop in progress: effective drain + grace, clamped under
launchd's live ``ExitTimeOut`` minus the dump margin. Lives here (not in gateway.restart)
because restart.py cannot import shutdown_watchdog without a cycle.)r   r   r   r)   s   &r!   Ú_effective_watchdog_leashrc   ¦   s   € ô )¨Ô1PÔQmÐntÓQuÓ1vÓwÐwr#   c            
      ó  € ] tR t^­tRt]P                   ! R R4      4       tR R lt]	R R l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 R l4       tR R ltR  R! ltR" R# lt]	R$ 4       tR% R& ltR' R( ltR) R* ltR+ R, ltR- R. ltR/ tR½R0 R1 llt R2 R3 lt!R4 R5 lt"R6 R7 lt#R8 R9 lt$R: R; lt%R< R= lt&R¾R> R? llt'R@RARBRARCRARDRARE](/RF RG llt)RHRI/RJ RK llt*RL RM lt+RN RO lt,R¿RP RQ llt-RR RS lt.RT RU lt/RV RW lt0RX RY lt1R¿RZ R[ llt2R\ R] lt3R^ R_ lt4R` Ra lt5]	R¿Rb Rc ll4       t6Rd Re lt7]	Rf Rg l4       t8Rh Ri lt9Rj Rk lt:Rl Rm lt;RnRIRoRA/Rp Rq llt<Rr Rs lt=Rt Ru lt>]	Rv Rw l4       t?Rx Ry lt@Rz R{ ltAR| R} ltB]	R~ R l4       tC]	R€ R� l4       tDR‚ Rƒ ltER„ R… ltFR† R‡ ltGRˆ R‰ ltHRŠ R‹ ltIRŒ R� ltJRŽ R� ltKR� R‘ ltLR’ R“ ltMR”R•R–R•/R— R˜ lltNR™ Rš ltOR› Rœ ltP]	R� Rž l4       tQ]	RŸ R  l4       tR]	R¡ R¢ l4       tSR£ R¤ ltTR¥ R¦ ltUR§ R¨ ltVR© Rª ltWR« R¬ ltXR­ R® ltYR¯ R° ltZR± R² lt[R³ R´ lt\RµR•R¶R•R·R•/R¸ R¹ llt]Rº R» lt^R¼t_RA# )ÀÚGatewayShutdownMixinzWStop/drain/restart, scale-to-zero and active-work accounting methods for GatewayRunner.c                  óš   € ] tR t^°t$ RtR]R&   RtR]R&   ]P                  ! ]	R7      t
R]R	&   R
tR]R&   RtR]R&   ^ tR]R&   R R ltRtR# )Ú!GatewayShutdownMixin._StopContextzDState threaded through the ``_stop_*`` phases of one ``stop()`` run.úCallable[[], int]Údeferred_countNúOptional[float]Ú
started_at)Údefault_factoryÚdictÚactive_agentsFr   Ú	timed_outç        ra   Údrain_elapsedr8   Úapi_livec               ó   € V ^8„  d   QhRR/# ©r   r   ra   r   )r    s   "r!   r"   Ú.GatewayShutdownMixin._StopContext.__annotate__½   s   € ÷ 	6ñ 	6˜Uñ 	6r#   c                	óN   € \         P                  ! 4       V P                  ,
          # ©N)ÚtimeÚ	monotonicrk   ©Úselfs   &r!   ÚelapsedÚ)GatewayShutdownMixin._StopContext.elapsed½   s   € Ü—>’>Ó# d§o¡oÕ5Ð5r#   r   )Ú__name__Ú
__module__Ú__qualname__Ú__firstlineno__Ú__doc__Ú__annotations__rk   ÚdataclassesÚfieldrm   rn   ro   rq   rr   r|   Ú__static_attributes__r   r#   r!   Ú_StopContextrg   °   sT   ‡ áRà)Ó)Ø&*ˆ
�OÓ*Ø)×/Ò/ÀÔEˆ�tÓEØˆ	�4ÓØ"ˆ�uÓ"ð ˆ�#Ó÷	6ñ 	6r#   r‡   c               ó   € V ^8„  d   QhRR/# ©r   r   r8   r   )r    s   "r!   r"   Ú!GatewayShutdownMixin.__annotate__Á   s   € ÷ 
ñ 
 Cñ 
r#   c                óÀ   € V P                  4       \        V 4      ,           V P                  4       ,           V P                  4       ,           V P	                  4       ,           # )z>All agent work the gateway must expose and drain as one total.©Ú_running_agent_countr   Ú_active_cron_job_countÚ_active_api_run_countÚ#_active_deferred_agent_worker_countrz   s   &r!   Ú_active_work_countÚ'GatewayShutdownMixin._active_work_countÁ   sY   € ð ×%Ñ%Ó'Ü  Ó&õ'à×)Ñ)Ó+õ,ð ×(Ñ(Ó*õ+ð ×6Ñ6Ó8õ	9ð	
r#   c               ó   € V ^8„  d   QhRR/# r‰   r   )r    s   "r!   r"   rŠ   Ì   s   € ÷ 	*ñ 	* Sñ 	*r#   c                 	ó.   € ^ RI Hp  \        V ! 4       4      # )é    ©Úget_running_job_ids)Úcron.schedulerr—   Úlenr–   s    r!   Ú_running_cron_job_countÚ,GatewayShutdownMixin._running_cron_job_countË   s   € õ 	7ÜÑ&Ó(Ó)Ð)r#   c               ó   € V ^8„  d   QhRR/# r‰   r   )r    s   "r!   r"   rŠ   ×   s   € ÷ ñ ¨ñ r#   c                óH   €  V P                  4       #   \         d     ^ # i ; i)uÁ  Cron jobs currently executing â€” they run outside ``_running_agents``; 0 if cron can't import.

Cron jobs run through a standalone ``AIAgent`` on the scheduler's own thread pool
(``cron/scheduler.py::run_job``), entirely outside ``self._running_agents`` â€” the dict every OTHER
active-work check on this class (``_running_agent_count``, ``_drain_active_agents``) reads. Without
this, the shutdown drain is structurally blind to in-flight cron work: it can report
``active_at_start=0`` and proceed straight to killing tool subprocesses while a cron job's terminal
command is still running (#60432). Best-effort: returns 0 if the cron module can't be imported (e.g.
a minimal test double for this class).
)rš   r<   rz   s   &r!   rŽ   Ú+GatewayShutdownMixin._active_cron_job_count×   s(   € ð	Ø×/Ñ/Ó1Ð1øÜô 	Úð	ús   ‚ ’! !c               ó$   € V ^8„  d   QhRRRRRR/# )r   Únamer:   r@   r   r   r8   r   )r    s   "r!   r"   rŠ   ç   s&   € ÷ Eñ E Sð E°ð E¸ñ Er#   c           	     óÄ   € \        \        V R/ 4      P                  \        P                  4      VR4      p\	        V4      '       d   \        ^ \        V! V!  4      4      # ^ # )z¹Call the primary API-server adapter's ``name`` hook, clamped >= 0 (0 when the hook is absent).

Only the primary API server owns the HTTP listener, so only it is a source of this work.
ÚadaptersN)rH   Úgetr   Ú
API_SERVERÚcallableÚmaxr8   )r{   r    r@   Úhelpers   &&* r!   Ú_api_server_hookÚ%GatewayShutdownMixin._api_server_hookç   sS   € ô
 œ  z°2Ó6×:Ñ:¼8×;NÑ;NÓOÐQUÐW[Ó\ˆÜ-5°f×-=Ò-=Œs�1”c™& $™-Ó(Ó)ÐDÀ1ÐDr#   c               ó   € V ^8„  d   QhRR/# r‰   r   )r    s   "r!   r"   rŠ   ï   s   € ÷ ñ  sñ r#   c                óJ   €  V P                  R4      #   \         d     ^ # i ; i)z4API-server work that is outside ``_running_agents``.Úactive_agent_work_count)r¨   r<   rz   s   &r!   r�   Ú*GatewayShutdownMixin._active_api_run_countï   s+   € ð	Ø×(Ñ(Ð)BÓCÐCøÜô 	Úð	ús   ‚ “"¡"c               ó   € V ^8„  d   QhRR/# r‰   r   )r    s   "r!   r"   rŠ   ö   s   € ÷ ñ ¨#ñ r#   c                óh   €  ^ RI Hp \        ^ \        V! 4       4      4      #   \         d     ^ # i ; i)aV  API-server executor threads still inside an agent turn (#116535).

Module-level, like the cron registry above: the handler-side adapter count is already
unreachable here (``adapters`` was cleared a phase earlier) and, worse, drops on handler
cancellation while the worker thread lives on. Read live at the close gate instead of
snapshotting.
)Úapi_worker_live_count)Ú!gateway.platforms.api_server_runsr°   r¦   r8   r<   )r{   r°   s   & r!   Ú_active_api_worker_countÚ-GatewayShutdownMixin._active_api_worker_countö   s2   € ð	ÝOÜ�qœ#Ñ3Ó5Ó6Ó7Ð7øÜô 	Úð	ús   ‚" ¢1°1c               ó    € V ^8„  d   QhRRRR/# ©r   Úreasonr:   r   r8   r   )r    s   "r!   r"   rŠ     s   € ÷ ñ °ð ¸ñ r#   c                ó†   €  V P                  RV4      #   \         d"   p\        P                  RT4        Rp?^ # Rp?ii ; i)zcInterrupt API-server agents not in ``_running_agents`` (same set ``_active_api_run_count`` counts).Úinterrupt_active_runsz7Failed interrupting api_server runs during shutdown: %sN©r¨   r<   r'   Údebug)r{   r¶   rA   s   && r!   Ú_interrupt_api_server_runsÚ/GatewayShutdownMixin._interrupt_api_server_runs  s>   € ð	Ø×(Ñ(Ð)@À&ÓIÐIøÜô 	Ü�L‰LÐRÐTWÔXÝûð	ús   ‚ ”A Ÿ;»A c               ó   € V ^8„  d   QhRR/# r‰   r   )r    s   "r!   r"   rŠ     s   € ÷ ñ °3ñ r#   c                ó„   €  V P                  R4      #   \         d"   p\        P                  RT4        Rp?^ # Rp?ii ; i)zEPersist the shutdown boundary on API runs before the drain can await.Úmark_shutdown_requestedz8Failed marking api_server runs as shutdown-requested: %sNr¹   )r{   rA   s   & r!   Ú!_mark_api_runs_shutdown_requestedÚ6GatewayShutdownMixin._mark_api_runs_shutdown_requested  s<   € ð	Ø×(Ñ(Ð)BÓCÐCøÜô 	Ü�L‰LÐSÐUXÔYÝûð	ús   ‚ “?ž:º?c               ó   € V ^8„  d   QhRR/# r‰   r   )r    s   "r!   r"   rŠ     s   € ÷ Gñ G°Sñ Gr#   c                ó‚   € \        V RR4      p\        V\        4      '       g   ^ # \        R \	        V4       4       4      # )zYExecutor workers that outlived their gateway turn (e.g. a timed-out hygiene compression).Ú_deferred_agent_workersNc              3  óT   "  € T F  qP                  4       '       d   K  ^x € K   	  R# 5i)é   N)Údone)Ú.0Úfutures   & r!   Ú	<genexpr>ÚKGatewayShutdownMixin._active_deferred_agent_worker_count.<locals>.<genexpr>  s   é € ÐF¡=˜¿¹¿—1’1£=ùs   ‚(ž
()rH   Ú
isinstancerm   ÚsumÚlist)r{   Úworkerss   & r!   r�   Ú8GatewayShutdownMixin._active_deferred_agent_worker_count  s6   € ä˜$Ð 9¸4Ó@ˆÜ˜'¤4×(Ò(ÙÜÑF¤4¨¤=ÓFÓFÐFr#   c               ó$   € V ^8„  d   QhRRRRRR/# )r   rÉ   úasyncio.FutureÚagentr   r   ÚNoner   )r    s   "r!   r"   rŠ     s"   € ÷ 2ñ 2°>ð 2È#ð 2ÐRVñ 2r#   c                óz   a€ \        V RR4      oSf
   / ;oV n        VSV&   R V3R llpVP                  V4       R# )zCExpose an executor worker to drain/interrupt until it really exits.rÄ   Nc               ó    € V ^8„  d   QhRRRR/# )r   Údone_futurerÒ   r   rÔ   r   )r    s   "r!   r"   ÚGGatewayShutdownMixin._track_deferred_agent_worker.<locals>.__annotate__"  s   € ÷ 	,ñ 	,¨ð 	,¸Dñ 	,r#   c                óä   <€ SP                  V R 4       V P                  4       '       g3   \        \        4      ;_uu_ 4        V P	                  4        R R R 4       R # R #   + '       g   i     R # ; irw   )ÚpopÚ	cancelledr   r<   Ú	exception)r×   rÏ   s   &€r!   Ú_discard_workerÚJGatewayShutdownMixin._track_deferred_agent_worker.<locals>._discard_worker"  sP   ø€ Ø�K‰K˜ TÔ*ð ×(Ñ(×*Ò*Üœi×(Õ(Ø×)Ñ)Ô+÷ )Ñ(ñ +ß(×(Ð(ús   ÁAÁA/	)rH   rÄ   Úadd_done_callback)r{   rÉ   rÓ   rÝ   rÏ   s   &&& @r!   Ú_track_deferred_agent_workerÚ1GatewayShutdownMixin._track_deferred_agent_worker  sG   ø€ ä˜$Ð 9¸4Ó@ˆØŠ?Ø57Ð7ˆG�dÔ2Øˆ�‰÷	,ð 	,ð 	× Ñ  Ö1r#   c               ó    € V ^8„  d   QhRRRR/# rµ   r   )r    s   "r!   r"   rŠ   -  s   € ÷ ñ ¸ð Àñ r#   c                óÎ  € ^ RI HpHp \        V RR4      p\	        V\
        4      '       g   ^ # ^ p\        4       p\        VP                  4       4       F_  w  rxVP                  4       '       g   Ve   \        V4      V9   d   K1  VP                  \        V4      4        V! W�VR7       V^,          pKa  	  V#   \         d"   p	\        P                  RT	4        Rp	?	KŒ  Rp	?	ii ; i)z<Request cancellation of detached executor-backed agent work.)Ú'_INTERRUPT_TOOL_REASON_GATEWAY_SHUTDOWNÚrequest_hard_interruptrÄ   N©Útool_reasonz=Failed interrupting deferred agent worker during shutdown: %s)Úgateway.runrä   rå   rH   rÌ   rm   ÚsetrÎ   ÚitemsrÇ   ÚidÚaddr<   r'   rº   )
r{   r¶   rä   rå   rÏ   ÚinterruptedÚseenrÉ   rÓ   rA   s
   &&        r!   Ú!_interrupt_deferred_agent_workersÚ6GatewayShutdownMixin._interrupt_deferred_agent_workers-  s¾   € ç_Ü˜$Ð 9¸4Ó@ˆÜ˜'¤4×(Ò(ÙØˆÜ›ˆÜ! '§-¡-£/Ö2‰MˆFØ�{‰{�}Š} ¢´°E³¸dÔ1BÙØ�H‰H”R˜“YÔðcÙ& uÐBiÕjØ˜qÕ ’ñ 3ð Ðøô ô cÜ—‘Ð\Ð^a×bÒbûðcús   ÂB8Â8C$ÃCÃC$c               ó   € V ^8„  d   QhRR/# r   r   )r    s   "r!   r"   rŠ   A  s   € ÷ ñ ¸ñ r#   c                óž  € \         ;QJ d&    R V P                   4       F  '       g   K   RM	  RM! R V P                   4       4      '       d   R# R R lpR R lpRV3R	V33 FP  w  r4\        \        P                  R
V R2RR7      ;_uu_ 4        V! 4       '       d    RRR4        R#  RRR4       KR  	  R#   + '       g   i     Kg  ; i)a  Live background work (delegations, processes, pending watchers) that must block a suspend.

PERMANENT supervised watchers (_hermes_supervised_watcher, incl. the scale-to-zero watcher
itself) are excluded, else this would be True forever and the gateway could never go dormant.
c              3  ó‚   "  € T F5  pVP                   ! 4       '       * ;'       d    \        VR R4      '       * x € K7  	  R# 5i)Ú_hermes_supervised_watcherFN)rÇ   rH   )rÈ   r   s   & r!   rÊ   ÚOGatewayShutdownMixin._scale_to_zero_has_live_background_work.<locals>.<genexpr>G  s7   é € ð 
á+�ð —’“ŒL×PÐP¤¨Ð,HÈ%Ó!PÔPÔPÛ+ùs   ‚!?¤?TFc               ó   € V ^8„  d   QhRR/# r   r   )r    s   "r!   r"   ÚRGatewayShutdownMixin._scale_to_zero_has_live_background_work.<locals>.__annotate__L  s   € ÷ 	&ñ 	& Tñ 	&r#   c                 ó"   € ^ RI Hp  V ! 4       ^ 8„  # )r•   ©Úactive_count)Útools.async_delegationrú   rù   s    r!   Ú_delegations_activeÚYGatewayShutdownMixin._scale_to_zero_has_live_background_work.<locals>._delegations_activeL  s   € Ý;Ù“> AÑ%Ð%r#   c               ó   € V ^8„  d   QhRR/# r   r   )r    s   "r!   r"   r÷   P  s   € ÷ 	`ñ 	` 4ñ 	`r#   c                 óh   € ^ RI Hp  \        V P                  4       ;'       g    V P                  4      # )r•   ©Úprocess_registry)Útools.process_registryr  r   Úhas_any_activeÚpending_watchersr   s    r!   Ú_processes_activeÚWGatewayShutdownMixin._scale_to_zero_has_live_background_work.<locals>._processes_activeP  s)   € Ý?ÜÐ(×7Ñ7Ó9×^Ð^Ð=M×=^Ñ=^Ó_Ð_r#   zasync-delegationzbg-workzscale-to-zero z check failed©r5   N)ÚanyÚ_background_tasksrB   ÚloggingÚDEBUG)r{   rü   r  ÚlabelÚprobes   &    r!   Ú'_scale_to_zero_has_live_background_workÚ<GatewayShutdownMixin._scale_to_zero_has_live_background_workA  s½   € ÷ ‹3ñ 
à×+Ò+ó
�3�3Š3ñ 
à×+Ò+ó
÷ 
ò 
ñ õ	&õ	`ð 1Ð2EÐFÈÐTeÐHfÓg‰LˆEÜ ¤§¡°.ÀÀÀ}Ð0UÐ`d×eÖeÙ—7’7Ø÷ fÒeØ÷ fÑeñ hñ ÷ f×eÐeús   ÂB;Â;Cc               ó    € V ^8„  d   QhRRRR/# )r   r    r:   r   úOptional[dict]r   )r    s   "r!   r"   rŠ   [  s   € ÷ 	>ñ 	> 3ð 	>¨>ñ 	>r#   c                ó  € ^ RI Hp  V! 4       p\        V\        4      '       d   VP	                  R4      MRp\        V\        4      '       d   VP	                  V 4      MRp\        T\        4      '       d   T# R#   \
         d     R# i ; i)zT``gateway.<name>`` from the user config when it is a dict, else None (never raises).)Ú_load_gateway_configÚgatewayN)rè   r  rÌ   rm   r£   r<   )r    r  Úuser_cfgÚgwÚsections   &    r!   Ú_gateway_cfg_sectionÚ)GatewayShutdownMixin._gateway_cfg_sectionZ  sx   € õ 	5ð	Ù+Ó-ˆHÜ,6°xÄ×,FÒ,F�—‘˜iÔ(ÈDˆBÜ&0°´T×&:Ò&:�b—f‘f˜T”lÀˆGô % W¬d×3Ò3ˆwÐ=¸Ð=øô ô 	Úð	ús   ˆAA; Á;B
Â	B
c               ó   € V ^8„  d   QhRR/# rt   r   )r    s   "r!   r"   rŠ   f  s   € ÷ \ñ \°Uñ \r#   c                	óz   € ^ RI Hp V P                  R4      pT! V'       d   VP                  R4      4      # R4      # )r•   )Úparse_idle_timeout_secondsÚscale_to_zeroÚidle_timeout_minutesN)Úgateway.scale_to_zeror  r  r£   )r{   r  Ústzs   &  r!   Ú#_scale_to_zero_idle_timeout_secondsÚ8GatewayShutdownMixin._scale_to_zero_idle_timeout_secondsf  s5   € ÝDØ×'Ñ'¨Ó8ˆÙ)ÏS¨#¯'©'Ð2HÓ*IÓ[Ð[ÐVZÓ[Ð[r#   c               ó   € V ^8„  d   QhRR/# ©r   r   rP   r   )r    s   "r!   r"   rŠ   k  s   € ÷ 
ñ 
¨Eñ 
r#   c                óÔ   a€ ^ RI Hp V P                  R4      ;'       g    / oR V3R llpV! RVP                  RR7      V! RVP                  R	R7      V! R
VP
                  R	R7      3# )zâ``(max_restarts, window_seconds, max_gap_seconds)`` for the restart-loop breaker.

``max_restarts <= 0`` disables it; ``max_gap_seconds`` is the longest spacing between
restart-interrupted boots that still counts as one loop.
)Úrestart_loop_guardr&  c               ó(   € V ^8„  d   QhRRRRRRRR/# )r   Úkeyr:   rZ   r8   Úpositiver   r   r   )r    s   "r!   r"   ÚEGatewayShutdownMixin._restart_loop_guard_config.<locals>.__annotate__t  s(   € ÷ 	ñ 	˜ð 	 sð 	°dð 	¸sñ 	r#   c                óx   <€ SP                  V 4      p\        V\        4      '       d   V^ 8”  g	   V'       g   V# V# ©r•   )r£   rÌ   r8   )r(  rZ   r)  ÚvalueÚrlgs   &&& €r!   Ú_int_orÚ@GatewayShutdownMixin._restart_loop_guard_config.<locals>._int_ort  s/   ø€ Ø—G‘G˜C“LˆEÜ˜%¤×%Ò%¨5°1¬9¿HØ�ØˆNr#   Úmax_restartsF)r)  Úwindow_secondsTÚmax_gap_seconds)r  r&  r  ÚDEFAULT_MAX_RESTARTSÚDEFAULT_WINDOW_SECONDSÚDEFAULT_MAX_GAP_SECONDS)r{   Ú_rlgr/  r.  s   &  @r!   Ú_restart_loop_guard_configÚ/GatewayShutdownMixin._restart_loop_guard_configk  so   ø€ õ 	7Ø×'Ñ'Ð(<Ó=×CÐCÀˆ÷	ð 	ñ �N D×$=Ñ$=ÈÔNÙÐ$ d×&AÑ&AÈDÔQÙÐ% t×'CÑ'CÈdÔSð
ð 	
r#   c               ó   € V ^8„  d   QhRR/# ©r   r   rÎ   r   )r    s   "r!   r"   rŠ   €  s   € ÷ 'ñ '¸4ñ 'r#   c                óä  aa€ \         P                  \         P                  \         P                  0o. oR VV3R llp V P                  '       dK   V P                  P
                  P                  4        F"  w  r#\        VRR4      '       g   K  V! V4       K$  	  \        V R/ 4      ;'       g    /  F  pV! V4       K  	  \        V R/ 4      ;'       g    / P                  4        F  pV F  pV! V4       K  	  K  	  \        V R/ 4      ;'       g    / P                  4        F  pT;'       g    /  F  pV! V4       K  	  K   	  S#   \         d    \        P                  RR	R
7       R.u # i ; i)aÕ  Return every enabled or live messaging platform across served profiles.

``self.config`` belongs to the launch profile, while ``_profile_adapters`` holds
live adapters for multiplexed secondary profiles. A direct secondary connection
must block suspension just like a direct primary connection. A secondary adapter
parked in ``_profile_failed_platforms`` (popped from ``_profile_adapters`` while a
retryable fatal reconnects) is still served: suspending mid-reconnect would leave
that reconnect unable to complete, so pending reconnects count as active too.

config.platforms is pre-seeded with disabled placeholders, and the api_server is
force-enabled on every hosted container (counting it silently disarmed the feature).
c               ó    € V ^8„  d   QhRRRR/# )r   Úplatformr   r   rÔ   r   )r    s   "r!   r"   ÚTGatewayShutdownMixin._scale_to_zero_active_messaging_platforms.<locals>.__annotate__�  s   € ÷ 	(ñ 	( 8ð 	(°ñ 	(r#   c                óN   <€ V S9  d   V S9  d   SP                  V 4       R # R # R # rw   )Úappend)r>  ÚactiveÚnon_messagings   &€€r!   Úadd_platformÚTGatewayShutdownMixin._scale_to_zero_active_messaging_platforms.<locals>.add_platform�  s'   ø€ Ø˜}Ô,°ÀÔ1GØ—‘˜hÖ'ñ 2HÑ,r#   ÚenabledFr¢   Ú_profile_adaptersÚ_profile_failed_platformsuF   scale-to-zero: active messaging platforms unreadable â€” staying awakeTr  z<unavailable>)r   ÚLOCALr¤   ÚWEBHOOKÚconfigÚ	platformsrê   rH   Úvaluesr<   r'   rº   )r{   rD  r>  Úplatform_configÚprofile_adaptersÚprofile_pendingrB  rC  s   &     @@r!   Ú)_scale_to_zero_active_messaging_platformsÚ>GatewayShutdownMixin._scale_to_zero_active_messaging_platforms€  sX  ù€ ô "Ÿ™¬×)<Ñ)<¼h×>NÑ>NÐOˆØˆ÷	(ñ 	(ð	%Ø�{�{ˆ{Ø15·±×1FÑ1F×1LÑ1LÖ1NÑ-�HÜ˜°	¸5×AÔAÙ$ XÖ.ñ 2Oô $ D¨*°bÓ9×?Ð?¸RÒ?�Ù˜XÖ&ñ @ä%,¨TÐ3FÈÓ%K×%QÐ%QÈr×$YÑ$YÖ$[Ð Û 0�HÙ  Ö*ó !1ñ %\ô %,¨DÐ2MÈrÓ$R×$XÐ$XÐVX×#`Ñ#`Ö#b�Ø /× 5Ð 5°2Ò 5�HÙ  Ö*ó !6ñ $cð ˆøô ô 	%Ü�L‰LØXØð ô ð $Ð$Ò$ð	%ús5   ¿E Á9E ÂE Â/%E Ã<E ÄE Ä/E Å%E/Å.E/c                 	óB   € ^ RI Hp   V ! 4       #   \         d     R# i ; i)r•   ©Úrelay_wake_urlN)Úgateway.relayrU  r<   rT  s    r!   Ú_relay_wake_url_or_noneÚ,GatewayShutdownMixin._relay_wake_url_or_none©  s%   € å0ð	Ù!Ó#Ð#øÜô 	Úð	ús   ˆ ��c               ó   € V ^8„  d   QhRR/# r   r   )r    s   "r!   r"   rŠ   ±  s   € ÷ 
ñ 
¨4ñ 
r#   c                óz   € ^ RI HpHpHp V! V! 4       V! V P	                  4       4      V P                  4       R7      # )u4   Whether to start the idle watcher (D1/D11/Â§3.4(1)).)Ú!messaging_is_relay_only_or_absentÚscale_to_zero_enabledÚ
should_arm)rF  Úrelay_only_or_absentÚwake_url)r  r[  r\  r]  rQ  rW  )r{   r[  r\  r]  s   &   r!   Ú_scale_to_zero_should_armÚ.GatewayShutdownMixin._scale_to_zero_should_arm±  s8   € çnÑnÙÙ)Ó+Ù!BÀ4×CqÑCqÓCsÓ!tØ×1Ñ1Ó3ô
ð 	
r#   c               ó   € V ^8„  d   QhRR/# ©r   r   rÔ   r   )r    s   "r!   r"   rŠ   º  s   € ÷ Zñ Z°Tñ Zr#   c                óf  € ^ RI HpHp  V! 4       '       g   R# V P                  4        Uu. uF  p\	        VRV4      NK  	  pp\
        P                  RV! V4      T;'       g    RV P                  4       '       d   RMR4       R# u upi   \         d    \
        P                  RR	R
7        R# i ; i)uT   One INFO line on why the idle watcher did NOT arm â€” only for an OPTED-IN instance.)r[  r\  Nr-  u›   scale-to-zero: NOT armed despite opt-in â€” relay_only_or_absent=%s (enabled platforms=%s), wake_url=%s. Need relay-only messaging + a registered wake URL.Únoneré   ÚMISSINGz.scale-to-zero: not-armed reason logging failedTr  )
r  r[  r\  rQ  rH   r'   r2   rW  r<   rº   )r{   r[  r\  ÚprB  s   &    r!   Ú#_log_scale_to_zero_not_armed_reasonÚ8GatewayShutdownMixin._log_scale_to_zero_not_armed_reasonº  s¡   € çbð	ZÙ(×*Ò*ÙØ6:×6dÑ6dÔ6fÓgÑ6f°”g˜a ¨!Ö,Ñ6fˆFÐgÜ�K‰KðRá1°&Ó9¸6×;KÐ;KÀVØ×5Ñ5×7Ò7‘¸Yö	ùò høô ô 	ZÜ�L‰LÐIÐTXˆL×Yð	Zús3   ŠB
 ™B
 «BÁ!B
 Á#B
 Á;B
 ÂB
 Â
"B0Â/B0c               ó   € V ^8„  d   QhRR/# r   r   )r    s   "r!   r"   rŠ   Ê  s   € ÷ 
ñ 
¨ñ 
r#   c                	ó’  a € ^ RI Hp R R lpV! RS P                  ^4      pV! RV 3R l^4      pS P                  p^ RI Hp V! RV\
        P
                  ! 4       4      pVe	   Wu8”  d   TpV! S P                  4       V,           V,           \
        P
                  ! 4       V,
          S P                  4       S P                  4       R	7      # )
r•   )Úis_idlec               ó(   € V ^8„  d   QhRRRRRRRR/# )r   r  r:   ÚfnúCallable[[], Any]Úbusy_sentinelr   r   r   )r    s   "r!   r"   ÚAGatewayShutdownMixin._scale_to_zero_is_idle.<locals>.__annotate__Ï  s*   € ÷ 	%ñ 	% #ð 	%Ð+<ð 	%ÈSð 	%ÐUXñ 	%r#   c                óh   €  V! 4       #   \          d    \        P                  R T RR7       Tu # i ; i)u.   scale-to-zero: %s unreadable â€” staying awakeTr  ©r<   r'   rº   )r  rn  rp  s   &&&r!   Ú_read_or_awakeÚCGatewayShutdownMixin._scale_to_zero_is_idle.<locals>._read_or_awakeÏ  s7   € ð%Ù“t�øÜô %Ü—‘ÐMÈuÐ_c�ÔdØ$Ò$ð%ús   ‚	 ‰%1°1zcron work countzapi work countc                 ó&   <€ S P                  R 4      # )r¬   )r¨   rz   s   €r!   Ú<lambda>Ú=GatewayShutdownMixin._scale_to_zero_is_idle.<locals>.<lambda>×  s   ø€ ¸T×=RÑ=RÐSlÔ=mr#   )Údashboard_client_last_seenzdashboard heartbeat)Úactive_work_countÚseconds_since_last_inboundÚidle_timeout_secondsÚhas_live_background_work)	r  rl  rš   Ú_last_inbound_atry  rx   r�   r!  r  )r{   rl  rt  Ú
cron_countÚ	api_countÚlast_inboundry  rî   s   f       r!   Ú_scale_to_zero_is_idleÚ+GatewayShutdownMixin._scale_to_zero_is_idleÊ  s·   ø€ Ý1õ	%ñ $Ð$5°t×7SÑ7SÐUVÓWˆ
Ù"Ð#3Ô5mÐopÓqˆ	ð ×,Ñ,ˆÝDÙÐ3Ð5OÔQU×QZÒQZÓQ\Ó]ˆØÒ Ô 3ØˆLÙØ"×7Ñ7Ó9¸JÕFÈÕRÜ'+§y¢y£{°\Õ'AØ!%×!IÑ!IÓ!KØ%)×%QÑ%QÓ%Sô	
ð 	
r#   c               ó   € V ^8„  d   QhRR/# rc  r   )r    s   "r!   r"   rŠ   æ  s   € ÷ 	5ñ 	5°$ñ 	5r#   c                ó°   € \         P                   ! 4       V n        \        V RR4      ^ 8”  d*   V P                  V P	                  4       R4       RV n        R# R# )z¶Stamp real inbound and flip status back to running after a dormant wake.

Internal completion/replay events deliberately do not call this (they must not keep an idle
gateway awake).
Ú_scale_to_zero_cooldown_untilrp   ú$scale-to-zero: status restore failedN)rx   r~  rH   Ú_scale_to_zero_statusÚ_serving_stater†  rz   s   &r!   Ú _scale_to_zero_note_real_inboundÚ5GatewayShutdownMixin._scale_to_zero_note_real_inboundæ  sL   € ô !%§	¢	£ˆÔÜ�4Ð8¸#Ó>ÀÔBØ×&Ñ& t×':Ñ':Ó'<Ð>dÔeØ14ˆDÖ.ñ Cr#   c               ó$   € V ^8„  d   QhRRRRRR/# )r   Ústater:   Úfail_msgr   rÔ   r   )r    s   "r!   r"   rŠ   ñ  s!   € ÷ 2ñ 2¨3ð 2¸#ð 2À$ñ 2r#   c                ó|   €  V P                  V4       R#   \         d    \        P                  TRR7        R# i ; i)zNBest-effort runtime status write; failures are debug-logged with ``fail_msg``.Tr  N)Ú_update_runtime_statusr<   r'   rº   )r{   r�  rŽ  s   &&&r!   rˆ  Ú*GatewayShutdownMixin._scale_to_zero_statusñ  s4   € ð	2Ø×'Ñ'¨Ö.øÜô 	2Ü�L‰L˜¨DˆL×1ð	2ús   ‚ •";º;c                óT   € V P                   P                  \        P                  4      # )zHReturn the connected RELAY adapter, if any (the one go_dormant targets).)r¢   r£   r   ÚRELAYrz   s   &r!   Ú_relay_adapter_for_dormancyÚ0GatewayShutdownMixin._relay_adapter_for_dormancyø  s   € à�}‰}× Ñ ¤§¡Ó0Ð0r#   c               ó    € V ^8„  d   QhRRRR/# ©r   Úintervalra   r   rÔ   r   )r    s   "r!   r"   rŠ   ü  s"   € ÷ `Uñ `U°Uð `UÀdñ `Ur#   c              ƒ  óÔ  "  € ^ RI Hp \        P                  ! \	        VR4      4      G Rj  x€L
  V P
                  '       Ed°    \        P                  ! V4      G Rj  x€L
  V P
                  '       g   R# \        P                  ! 4       V P                  8  g   V P                  4       '       g   K‚  V P                  4       pV! V4      '       gH   V P                  '       g4   RV n
        \        P                  RRP                  R V 4       4      4       Kç  RV n
        \        V P                  4       R	R4      p\!        V4      '       g   EK  ^ R
I Hp V! 4       '       g2   V P$                  '       g   RV n        \        P                  R4       EKa  \        P                  RV P'                  4       4       V P)                  RR4       V P+                  R4      '       g)   \        P-                  R4       V P/                  4        EKÖ  Rp V! 4       p\        P0                  ! V4      '       d   VG Rj  x€L
 pVRJd   Rp\        P-                  R4       \        P                  ! 4       \7        TR4      ,           T n        T'       g   T P/                  4        EKi  T P                  4       '       g)   \        P                  R4       T P/                  4        EK§  T P9                  4       G Rj  x€L
  EKÂ  R#  ELÉ ELš LÈ  \2         d    Rp\        P5                  RRR7        LÏi ; i L;  \        P:                   d    h \2         d    \        P5                  RRR7        EK3  i ; i5i)uÊ  Watch for idle, drive the relay dormant, then self-suspend. On sustained idle: status
`draining` (NOT _running=False), relay go_dormant() (socket close, NOT disconnect()), no
mark_resume_pending (suspend preserves RAM), THEN suspend via the flaps socket â€” Fly autostop
sees only INBOUND connections and would freeze mid-job. Without a flaps socket NAS brokers
the stop through the stamped GATEWAY_RELAY_SLEEP_URL; with no lever at all the watcher
abstains.)r[  ç      >@NTuÞ   scale-to-zero: idle, but directly connected messaging platform(s) %s hold a live socket that a suspended instance cannot wake from â€” staying awake. Route them through the relay connector or disable them to allow suspend.ú, c              3  óN   "  € T F  p\        \        VR V4      4      x € K  	  R# 5i)r-  N)r:   rH   )rÈ   rg  s   & r!   rÊ   Ú>GatewayShutdownMixin._scale_to_zero_watcher.<locals>.<genexpr>  s!   é € Ð%RÉ6Àa¤c¬'°!°W¸aÓ*@×&AÐ&AË6ùó   ‚#%FÚ
go_dormant)Úsuspend_availablez•scale-to-zero: idle, but this platform offers no suspend lever (no in-machine API and no brokered sleep URL); staying connected rather than quiescinguo   scale-to-zero: gateway idle for >= %.0fs â€” going dormant (relay buffered, socket closed) then self-suspendingÚdrainingz!scale-to-zero: status mark failedud   scale-to-zero: could not hold the relay re-dial â€” staying awake rather than suspending unprotectedui   scale-to-zero: connector did not ack going_idle â€” staying awake rather than freezing a live destinationz scale-to-zero: go_dormant failedr  ç      N@uB   scale-to-zero: inbound arrived during quiesce â€” skipping suspendz%scale-to-zero watcher iteration error)r  r[  ÚasyncioÚsleepÚminÚ_runningrx   r†  r‚  rQ  Ú%_scale_to_zero_direct_platform_loggedr'   r2   ÚjoinrH   r”  r¥   r   Ú _scale_to_zero_no_suspend_loggedr!  rˆ  Ú_scale_to_zero_hold_redialÚwarningÚ_scale_to_zero_abandon_suspendÚiscoroutiner<   rº   r¦   Ú_scale_to_zero_self_suspendÚCancelledError)r{   r˜  r[  rB  rŸ  r   Ú
dormant_okrD   s   &&      r!   Ú_scale_to_zero_watcherÚ+GatewayShutdownMixin._scale_to_zero_watcherü  sÑ  é € õ 	LÜ�mŠmœC ¨$Ó/Ó0×0Ð0Ø�m�m‰mðVUÜ—m’m HÓ-×-Ð-Ø—}—}�}ÙÜ—9’9“; ×!CÑ!CÔCÈ4×KfÑKf×KhÒKhÙð ×GÑGÓI�Ù8¸×@Ò@Ø×E×EÐEØEI˜ÔBÜŸ™ð'ð !ŸI™IÑ%RÉ6Ó%RÓRôñ Ø=B�Ô:Ü$ T×%EÑ%EÓ%GÈÐW[Ó\�
Ü 
×+Ò+Úõ DÙ(×*Ò*Ø×@×@Ð@Ø@D˜Ô=ÜŸ™ð-ôò
 Ü—‘ðKà×<Ñ<Ó>ôð
 ×*Ñ*¨:Ð7ZÔ[ð ×6Ñ6°t×<Ò<ô —N‘Nð6ôð ×7Ñ7Ô9ÚØ!�
ðTÙ'›\�FÜ×*Ò*¨6×2Ò2Ø'-§˜ð  TÓ)Ø%*˜
ÜŸ™ðFôô 6:·Y²Y³[Ä3ÀxÐQUÓCVÕ5V�Ô2÷ "Ø×7Ñ7Ô9ÚØ×2Ñ2×4Ò4Ü—K‘KÐ dÔeØ×7Ñ7Ô9ÚØ×6Ñ6Ó8×8Ó8ñg ò 	1ò .ñr ".øô !ô TØ!&�JÜ—L‘LÐ!CÈd�LÖSðTúñ 9øÜ×)Ñ)ô ØÜô UÜ—‘ÐDÈt�×TÐTðUüs  ‚*M(¬K5­M(ÁL) ÁK8ÁL) Á5M(Á79L) Â1M(Â3L) ÃL) Ã#3L) ÄM(Ä3L) ÅM(ÅL) Å!L) Å3L) ÆM(ÆAL) Ç%L) ÈM(ÈL) È
"K= È-K= È2K;È3!K= ÉAL) ÊM(ÊL) Ê0%L) ËM(ËL) Ë+L'Ë,L) Ë0M(Ë8L) Ë;K= Ë=$L$Ì!L) Ì#L$Ì$L) Ì)M%ÍM%Í M(Í$M%Í%M(c               ó   € V ^8„  d   QhRR/# rc  r   )r    s   "r!   r"   rŠ   ^  s   € ÷ +2ñ +2°4ñ +2r#   c              ƒ  ó¢  "  € ^ RI HpHpHpHp  V! 4       '       dd   \
        P                  ! V4      G Rj  x€L
 pRpV'       d+   V P                  4       G Rj  x€L
  V P                  R4       MV P                  4        MnV! 4       pV'       g(   V P                  4        \        P                  R4       R# \
        P                  ! W'4      G Rj  x€L
 pRpV'       g   V P                  4        V'       g   \        P                  RV4       R# R#  LÙ L¹ LD  \         d,    \        P                  RR	R
7       T P                  4         R# i ; i5i)z¡Suspend this machine, in-guest where possible and via NAS otherwise (fail-awake).

Called ONLY after a clean, acked go_dormant(), with the re-dial already held.
)Úbrokered_sleep_urlÚrequest_brokered_suspendÚself_suspend_availableÚsuspend_selfNzself-suspendFuN   scale-to-zero: no suspend lever available â€” dormant without platform suspendzbrokered suspendug   scale-to-zero: %s not accepted â€” machine stays awake (fail-awake); will retry on the next idle windowz"scale-to-zero: self-suspend failedTr  )r  rµ  r¶  r·  r¸  r£  Ú	to_threadÚ_scale_to_zero_await_freeze_gaprª  r¬  r'   rº   r«  r<   )r{   rµ  r¶  r·  r¸  ÚacceptedÚleverÚurls   &       r!   r®  Ú0GatewayShutdownMixin._scale_to_zero_self_suspend^  s  é € ÷
	
ó 	
ð#	2Ù%×'Ò'Ü!(×!2Ò!2°<Ó!@×@�Ø&�ßð ×>Ñ>Ó@×@Ð@Ø×3Ñ3°EÕ:à×7Ñ7Õ9ñ )Ó*�ßð ×7Ñ7Ô9Ü—L‘LØhôñ ä!(×!2Ò!2Ð3KÓ!Q×Q�Ø*�ßØ×7Ñ7Ô9ßÜ—‘ð4Ø5:öñ ñ5 Añ
 Añ" Røô ô 	2Ü�L‰LÐ=ÈˆLÔMØ×/Ñ/×1ð	2üs€   ‚E�&D ¶D·D ÁD ÁDÁ5D Â%D Â4EÂ6D ÃDÃD ÃD Ã6D ÄEÄD ÄD ÄD Ä2EÅEÅEÅEc               ó   € V ^8„  d   QhRR/# rc  r   )r    s   "r!   r"   rŠ   ‹  s   € ÷ 9ñ 9°tñ 9r#   c              ƒ  óÎ   "  € ^ RI HpHp \        P                  ! 4       V,           p\        P                  ! 4       V8  d!   \        P
                  ! V4      G Rj  x€L
  K:  R#  L5i)a3  Hold the re-dial fence across the flaps-2xx -> kernel-freeze gap.

Sliced on the WALL clock rather than one ``asyncio.sleep`` because a Fly suspend stops
CLOCK_MONOTONIC while CLOCK_REALTIME keeps tracking host time. Measured on a Fly machine
(gru, 2026-09-03) across a 252.219s freeze: ``time.monotonic()`` advanced 0.501s,
``time.time()`` advanced 252.219s. ``asyncio.sleep`` runs on ``loop.time()`` (monotonic),
so a single sleep would resume with its REMAINDER after the wake and delay the drain
re-dial by exactly that much, on every wake of every Fly agent.

On the wall clock the deadline is already past by the time we resume, so the fence costs
nothing after a freeze while still spanning the full gap before one. That decoupling is
what lets FLY_FREEZE_GRACE_S be sized for the slowest (largest-RAM) machine.
)ÚFLY_FREEZE_GRACE_SÚFLY_FREEZE_GRACE_TICK_SN)r  rÁ  rÂ  rx   r£  r¤  )r{   rÁ  rÂ  Údeadlines   &   r!   rº  Ú4GatewayShutdownMixin._scale_to_zero_await_freeze_gap‹  sE   é € ÷ 	VÜ—9’9“;Ð!3Õ3ˆÜ�iŠi‹k˜HÔ$Ü—-’-Ð 7Ó8×8Ò8ñ %Ù8ùs   ‚AA%ÁA#Á	A%c               ó   € V ^8„  d   QhRR/# rc  r   )r    s   "r!   r"   rŠ   ž  s   € ÷ 
ñ 
°ñ 
r#   c                ó  € V P                  R4       \        V RR4      '       g3   V P                  '       d!   V P                  V P	                  4       R4       \        V P                  \        P                  ! 4       R,           4      V n        R# )z÷Undo a quiesce we are not going to follow with a suspend.

All three together: a released supervisor still advertising `draining` reads as
mid-shutdown until the next real inbound event, and an abort that skips the cooldown
re-runs on every tick.
FÚ	_drainingr‡  r¢  N)rª  rH   r¦  rˆ  r‰  r¦   r†  rx   rz   s   &r!   r¬  Ú3GatewayShutdownMixin._scale_to_zero_abandon_suspendž  sh   € ð 	×'Ñ'¨Ô.ô �t˜[¨%×0Ò0°T·]·]°]Ø×&Ñ& t×':Ñ':Ó'<Ð>dÔeä-0Ø×.Ñ.´·	²	³¸dÕ0Bó.
ˆÖ*r#   c               ó    € V ^8„  d   QhRRRR/# )r   Úheldr   r   r   )r    s   "r!   r"   rŠ   ¯  s   € ÷ ñ ¨tð ¸ñ r#   c                óæ   €  V P                  4       pVf   R# \        Y!'       d   RMRR4      p\        V4      '       g   R# V! 4       RJ #   \         d    \        P                  RRR7        R# i ; i)z³Hold or release the relay's reconnect supervisor. Returns whether the transport actually
took it, so the caller can refuse to suspend without the protection rather than fail open.NFÚhold_redialÚrelease_redialTz(scale-to-zero: redial hold toggle failedr  )r”  rH   r¥   r<   r'   rº   )r{   rÊ  ÚadapterÚmethods   &&  r!   rª  Ú/GatewayShutdownMixin._scale_to_zero_hold_redial¯  sq   € ð	Ø×6Ñ6Ó8ˆGØŠÙÜ˜W¶t¡mÐAQÐSWÓXˆFÜ˜F×#Ò#Ùñ “8˜tÐ#Ð#øÜô 	Ü�L‰LÐCÈdˆLÔSÚð	ús   ‚A
 ˜'A
 ÁA
 Á
"A0Á/A0c               ó   € V ^8„  d   QhRR/# rc  r   )r    s   "r!   r"   rŠ   Â  s   € ÷ 
0ñ 
0 tñ 
0r#   c                ó¦   € V P                   '       d   R# RV n         \        P                  RV P                  4       4       V P	                  R4       R# )zXBegin external drain: refuse NEW turns (in-flight ones are NOT interrupted). Idempotent.NTu€   External drain ENGAGED (.drain_request.json present) â€” refusing new turns; %d in-flight turn(s) will finish. Process stays up.r¡  )Ú_external_drain_activer'   r2   r‘   r�  rz   s   &r!   Ú_enter_external_drainÚ*GatewayShutdownMixin._enter_external_drainÂ  sG   € à×&×&Ð&ÙØ&*ˆÔ#Ü�‰ðMØNR×NeÑNeÓNgô	
ð
 	×#Ñ# JÖ/r#   c               ó   € V ^8„  d   QhRR/# rc  r   )r    s   "r!   r"   rŠ   Î  s   € ÷ ;ñ ; dñ ;r#   c                ó8  € V P                   '       g   R# RV n         V P                  '       g   V P                  '       g   \        P	                  R4       R# \        P	                  RV P                  4       4       V P                  V P                  4       4       R# )z\Cancel external drain: re-accept new turns. Idempotent; never resurrects a stopping gateway.NFug   External drain marker cleared during shutdown â€” not reverting to running (shutdown takes precedence).uf   External drain RELEASED (.drain_request.json removed) â€” re-accepting new turns; gateway_state -> %s.)rÓ  rÇ  r¦  r'   r2   r‰  r�  rz   s   &r!   Ú_exit_external_drainÚ)GatewayShutdownMixin._exit_external_drainÎ  sv   € à×*×*Ð*ÙØ&+ˆÔ#Ø�>�>ˆ> §§ Ü�K‰Kð:ôñ Ü�‰ð;Ø<@×<OÑ<OÓ<Qô	
ð 	×#Ñ# D×$7Ñ$7Ó$9Ö:r#   c               ó    € V ^8„  d   QhRRRR/# r—  r   )r    s   "r!   r"   rŠ   ß  s   € ÷ *ñ *°Uð *ÀTñ *r#   c              ƒ  ó¾  "  € ^ RI Hp V P                  '       dx    \        P                  ! V4      G Rj  x€L
 '       d"   V P                  4        V P                  4        MV P                  4         \        P                  ! V4      G Rj  x€L
  K‰  R#  L^  \        P                   d    h \         d#   p\        P                  RTRR7        Rp?LaRp?ii ; i LO5i)zcPoll ``.drain_request.json`` at 1s: present -> enter drain, absent -> exit; a stale epoch = absent.)Údrain_requestedNz$Drain-control watcher tick error: %sTr  )Úgateway.drain_controlrÜ  r¦  r£  r¹  rÔ  Ú_persist_active_agentsrØ  r¯  r<   r'   rº   r¤  )r{   r˜  rÜ  rA   s   &&  r!   Ú_drain_control_watcherÚ+GatewayShutdownMixin._drain_control_watcherß  s°   é € å9Ø�m�mˆmðYô !×*Ò*¨?Ó;×;Ö;Ø×.Ñ.Ô0ð ×/Ñ/Õ1à×-Ñ-Õ/ô
 —-’- Ó)×)Ò)ñ ñ <øô ×)Ñ)ô ØÜô YÜ—‘ÐCÀSÐSW�×XÑXûðYúá)ùsb   ‚CœB µB¶	B Á  B Á CÁ!B Á1CÂCÂCÂB ÂCÂ5CÂ6CÃCÃCÃCÚplatform_stateNÚ
error_codeÚerror_messageÚneeds_attentionÚretrying_sincec               ó4   € V ^8„  d   QhRRRRRRRRRRR	R
RR/# )r   r>  r:   rá  rW   râ  rã  rä  zOptional[bool]rå  r   r   rÔ   r   )r    s   "r!   r"   rŠ   ó  sL   € ÷ 
ñ 
Øð
Ø0=ð
à!ð
à9Fð
ð (ð
ð ADð
ð 
ñ	
r#   c               	ób   € ^ RI Hp / pVe   WXR&   V\        Jd   WhR&   V! R	RVRVRVRV/VB  R# )
r•   )Ú_write_runtime_status_quietNrä  rå  r>  rá  râ  rã  r   )rè   rè  r   )	r{   r>  rá  râ  rã  rä  rå  rè  Úextras	   &&$$$$$  r!   Ú_update_platform_runtime_statusÚ4GatewayShutdownMixin._update_platform_runtime_statusó  s\   € õ
 	<Ø "ˆØÒ&Ø'6Ð#Ñ$Ø¤Ó'Ø&4Ð"Ñ#Ù#ñ 	
Øð	
Ø.<ð	
ØISð	
à'ð	
à+0ô	
r#   r¶   Ú c               ó    € V ^8„  d   QhRRRR/# ©r   r¶   r:   r   rÔ   r   )r    s   "r!   r"   rŠ     s   € ÷ 
ñ 
¸#ð 
Àtñ 
r#   c               ó�  € \        V R/ 4      P                  V4      pVe   VP                  R4      '       d   R# RVR&   T;'       g    RVR&   \        R4      VR&   V P                  VP                  RRVR,          R	7       \
        P                  R
VP                  VP                  R^ 4      VR,          VP                  4       R# )u\   Pause a queued platform (manual ``/platform pause`` only â€” the watcher never auto-pauses).Ú_failed_platformsNÚpausedTz#auto-paused after repeated failuresÚpause_reasonÚinfÚ
next_retry)rá  râ  rã  u¦   %s paused after %d consecutive failures (%s) â€” fix the underlying issue then run `/platform resume %s` to retry, or `hermes gateway restart` to restart the gateway.Úattempts)rH   r£   ra   rê  r-  r'   r«  )r{   r>  r¶   r2   s   &&$ r!   Ú_pause_failed_platformÚ+GatewayShutdownMixin._pause_failed_platform  s¸   € ä�tÐ0°"Ó5×9Ñ9¸(ÓCˆØŠ<˜4Ÿ8™8 H×-Ò-ÙØˆˆX‰Ø%×NÐNÐ)Nˆˆ^Ñä" 5›\ˆˆ\ÑØ×,Ñ,Ø�N‰N¨8ÀÐTXÐYgÕThð 	-ô 	
ô 	�‰ðWà�N‰N˜DŸH™H Z°Ó3°T¸.Õ5IÈ8Ï>É>ö	
r#   c               ó   € V ^8„  d   QhRR/# r   r   )r    s   "r!   r"   rŠ     s   € ÷ ñ °4ñ r#   c                óZ  € \        V R/ 4      P                  V4      pVe   VP                  R4      '       g   R# RVR&   VP                  RR4       ^ VR&   \        P                  ! 4       VR&   V P                  VP                  RR	7       \        P                  R
VP                  4       R# )z\Unpause a platform (reset attempts, retry on the next watcher tick). True iff it was paused.rð  Nrñ  Frò  rõ  rô  Úretrying)rá  u,   %s resumed â€” retrying on next watcher tickT)	rH   r£   rÚ   rx   ry   rê  r-  r'   r2   )r{   r>  r2   s   && r!   Ú_resume_paused_platformÚ,GatewayShutdownMixin._resume_paused_platform  s‘   € ä�tÐ0°"Ó5×9Ñ9¸(ÓCˆØŠ<˜tŸx™x¨×1Ò1ÙØˆˆX‰Ø�‰� Ô&ØˆˆZÑÜ!Ÿ^š^Ó-ˆˆ\ÑØ×,Ñ,¨X¯^©^ÈJÐ,ÔWÜ�‰ÐBÀHÇNÁNÔSÙr#   c               ó   € V ^8„  d   QhRR/# r$  r   )r    s   "r!   r"   rŠ   $  s   € ÷ 
ñ 
 Eñ 
r#   c                óž   € V P                  4       \        V 4      ,           V P                  4       V P                  4       V P	                  4       3# )z[``(agents, cron, api, deferred)`` the drain waits on; a managed-worker turn is a chat turn.rŒ   rz   s   &r!   Ú_drain_work_countsÚ'GatewayShutdownMixin._drain_work_counts$  sG   € ð ×%Ñ%Ó'Ô*<¸TÓ*BÕBÀD×D_ÑD_ÓDaØ×&Ñ&Ó(¨$×*RÑ*RÓ*Tð
ð 	
r#   c               ó$   € V ^8„  d   QhRRRRRR/# )r   Útimeoutra   Úcron_timeoutrj   r   ztuple[dict[str, Any], bool]r   )r    s   "r!   r"   rŠ   +  s$   € ÷ &#ñ &#Øð&#Ø,;ð&#à	$ñ&#r#   c              ƒ  	ó  a aa	a
aa"  € S P                  4       p\        P                  ! 4       oS P                  4       o
R oR
R V
VVV 3R lllpV! RR7       S P                  '       g   \        S
4      '       g   VR3# SP                  4       pWQ,           o	YRf   TMT,           oR VV	VV 3R llpV! 4       '       d(   V! 4        \        P                  ! R	4      G Rj  x€L
  K4  \        S P                  4       4      pV! RR7       W73#  L+5i)rp   Fc               ó    € V ^8„  d   QhRRRR/# )r   Úforcer   r   rÔ   r   )r    s   "r!   r"   Ú?GatewayShutdownMixin._drain_active_agents.<locals>.__annotate__3  s   € ÷ 	:ñ 	:¨ð 	:¸ñ 	:r#   c                ó°   <€ SP                  4       pSP                  4       pV '       g   VS8w  g   VS,
          R 8¼  d   SP                  R4       W!uooR# R# )ç      ð?r¡  N)rx   rÿ  r�  )r  ÚnowÚcountsÚlast_countsÚlast_status_atÚloopr{   s   &  €€€€r!   Ú_maybe_update_statusÚGGatewayShutdownMixin._drain_active_agents.<locals>._maybe_update_status3  sP   ø€ à—)‘)“+ˆCØ×,Ñ,Ó.ˆFß˜ +Ô-°#¸Õ2FÈ3Ô1NØ×+Ñ+¨JÔ7Ø.4Ð+�š^ñ 2Or#   T)r  Nc               ó   € V ^8„  d   QhRR/# r   r   )r    s   "r!   r"   r  F  s   € ÷ 	nñ 	n ñ 	nr#   c                 óÐ   <€ SP                  4       p SP                  4       w  rr4\        T;'       g    T;'       d    V S8  ;'       g    T;'       g    T;'       d    V S8  4      # rw   )rx   rÿ  r   )	r
  ÚagentsÚcronÚapiÚdeferredÚcron_deadlinerÃ  r  r{   s	        €€€€r!   Ú_still_drainingÚBGatewayShutdownMixin._drain_active_agents.<locals>._still_drainingF  s]   ø€ Ø—)‘)“+ˆCØ*.×*AÑ*AÓ*CÑ'ˆF˜#Ü˜&×,Ð, H×@Ð@°#¸±.×lÐlÀtÇ{À{Ès×FkÐFkÐX[Ð^kÑXkÓmÐmr#   çš™™™™™¹?)F)Ú_snapshot_running_agentsr£  Úget_running_looprÿ  Ú_running_agentsr  rx   r¤  )r{   r  r  Úsnapshotr  Ústartedr  ro   r  rÃ  r  r  r  s   f&&     @@@@@r!   Ú_drain_active_agentsÚ)GatewayShutdownMixin._drain_active_agents+  sç   ýé € ð ×0Ñ0Ó2ˆÜ×'Ò'Ó)ˆØ×-Ñ-Ó/ˆØˆ÷	:õ 	:ñ 	 4Õ(Ø×#×#Ð#¬C°×,<Ò,<Ø˜U�?Ð"ð —)‘)“+ˆØÕ$ˆØÒ.B¡7ÈÕUˆ÷	nó 	nñ ×ÒÙ Ô"Ü—-’- Ó$×$Ò$Ü˜×/Ñ/Ó1Ó2ˆ	Ù 4Õ(ØÐ"Ð"ñ %ùs   ˆB4DÂ= DÃD	Ã,Dc               ó    € V ^8„  d   QhRRRR/# rî  r   )r    s   "r!   r"   rŠ   S  s   € ÷ Oñ O°ð O¸ñ Or#   c           	     	ó  € ^ RI HpHpHp \	        V P
                  P                  4       4       FW  w  rVWbJ d   K  \        \        P                  R4      ;_uu_ 4        V! WaVR7       \        P                  RV4       RRR4       KY  	  V P                  V4      R3V P                  V4      R3\        W4      R3\        W4      R	33 F%  w  rxV'       g   K  \        P                  R
Wx4       K'  	  R#   + '       g   i     KÕ  ; i)r•   )Ú_AGENT_PENDING_SENTINELrä   rå   z-Failed interrupting agent during shutdown: %sræ   z8Interrupted running agent for session %s during shutdownNzapi_server run(s)zdeferred agent worker(s)zmanaged-worker turn(s)zside agent(s)z!Interrupted %d %s during shutdown)rè   r$  rä   rå   rÎ   r  rê   rB   r
  r  r'   rº   r»   rï   r   r   )	r{   r¶   r$  rä   rå   Úsession_keyrÓ   ÚcountÚwhats	   &&       r!   Ú_interrupt_running_agentsÚ.GatewayShutdownMixin._interrupt_running_agentsS  så   € ßxÑxÜ"& t×';Ñ';×'AÑ'AÓ'CÖ"DÑˆKØÓ/ÙÜ ¤§¡Ð0_×`Õ`Ù& uÐBiÕjÜ—‘ÐWÐYdÔe÷ aÑ`ñ #Eð ×,Ñ,¨VÓ4Ð6IÐJØ×3Ñ3°FÓ;Ð=WÐXÜ Ó-Ð/GÐHÔK`ÐaeÓKnÐpð  KAó
‰KˆE÷
 ‰uÜ—‘Ð@À%ÖNó
÷	 a×`Ð`ús   Á!C2Ã2Dc               ó   € V ^8„  d   QhRR/# )r   r   r:   r   )r    s   "r!   r"   rŠ   d  s   € ÷ tñ t¨Cñ tr#   c                	ó>   € ^ RI HpHp V P                  '       d   V# T# )r•   )Ú!_INTERRUPT_REASON_GATEWAY_RESTARTÚ"_INTERRUPT_REASON_GATEWAY_SHUTDOWN)rè   r,  r-  r1   )r{   r,  r-  s   &  r!   Ú_shutdown_interrupt_reasonÚ/GatewayShutdownMixin._shutdown_interrupt_reasond  s   € ßeØ48×4K×4KÐ4KÐ0ÐsÐQsÐsr#   c               ó    € V ^8„  d   QhRRRR/# )r   Ú
log_prefixr:   r   rÎ   r   )r    s   "r!   r"   rŠ   h  s   € ÷ ñ Àcð Èdñ r#   c              ƒ  óœ  "  € ^ RI Hp V P                  '       d   RMRp. p\        V P                  P                  4       4       Fl  w  rVWbJ d   K  \        \        P                  RW4      ;_uu_ 4        V P                  P                  WS4      G Rj  x€L
  VP                  V4       RRR4       Kn  	  V#  L#  + '       g   i     K…  ; i5i)zOMark every non-pending running session resume_pending; returns the keys marked.©r$  Úrestart_timeoutÚshutdown_timeoutz%s failed for %s: %sN)rè   r$  r1   rÎ   r  rê   rB   r
  r  Úasync_session_storeÚmark_resume_pendingrA  )r{   r1  r$  r¶   ÚmarkedÚ_skÚ_agents   &&     r!   Ú%_mark_running_sessions_resume_pendingÚ:GatewayShutdownMixin._mark_running_sessions_resume_pendingh  s¨   é € å7Ø&*×&=×&=Ð&=Ñ"ÐCUˆØˆô   × 4Ñ 4× :Ñ :Ó <Ö=‰KˆCØÓ0ÙÜ ¤§¡Ð0FÈ
×XÕXØ×.Ñ.×BÑBÀ3ÓO×OÐOØ—‘˜cÔ"÷ YÑXñ >ð ˆñ P÷ Y×XÐXüs0   ‚A1CÁ3B8ÂB6
ÂB8Â(CÂ6B8Â8C	Ã	Cc               ó    € V ^8„  d   QhRRRR/# )r   r>  r   r   r   r   )r    s   "r!   r"   rŠ   x  s   € ÷ Wñ W°hð WÀ4ñ Wr#   c                óŽ   € V P                   P                  P                  V4      pVRJ ;'       g    \        VP                  4      # )zKFalse when the platform config sets ``gateway_restart_notification=false``.N)rK  rL  r£   r   Úgateway_restart_notification)r{   r>  Úplatform_cfgs   && r!   Ú_restart_notification_allowedÚ2GatewayShutdownMixin._restart_notification_allowedx  s:   € à—{‘{×,Ñ,×0Ñ0°Ó:ˆØ˜tÐ#×VÐV¤t¨L×,UÑ,UÓ'VÐVr#   c               ó$   € V ^8„  d   QhRRRRRR/# )r   r>  r   r'  r:   r   r   r   )r    s   "r!   r"   rŠ   }  s"   € ÷ 
ñ 
¨ð 
¸ð 
ÐSWñ 
r#   c                ó®   € Vf   V P                  V4      M\        VP                  4      pV'       d   R# \        P	                  RW!P
                  4       R# )zº``_restart_notification_allowed`` with the INFO suppression line for shutdown notices.
``platform_cfg`` is a SERVED profile's own platform entry; ``self.config`` is the launch profile's.TzRShutdown notification suppressed for %s: %s has gateway_restart_notification=falseF)rA  r   r?  r'   r2   r-  )r{   r>  r'  r@  Úalloweds   &&&& r!   Ú_notice_allowedÚ$GatewayShutdownMixin._notice_allowed}  sO   € ð DPÒCW�4×5Ñ5°hÔ?Ü˜\×FÑFÓGð 	çÙÜ�‰Ø`Ðbf×hvÑhvô	
ñ r#   c               ó   € V ^8„  d   QhRR/# r‰   r   )r    s   "r!   r"   rŠ   ‰  s   € ÷ @ñ @¸cñ @r#   c              ƒ  ó0  a aaaaaaa"  € V'       g   ^ #  ^ RI Hp ^ RIHp \        S P                  '       d   RMR4      p\        4       oT EF‰  p T! T4      pT'       g   K  T! TRR7      pTP                  R
4      ;'       g    Tp	\        RY•R7      oT;'       g    R EF/  p
 \        \        T
P                  RR4      4      P                  4       4      oS P                  P                  S4      oSe   S P                  S4      '       g   Ko  \        T
P                  R4      4      oT
P                  R4      pT.\!        SP"                  ST4      O5oSS9   d   K¾  \%        \&        P(                  RSP"                  S4      ;_uu_ 4        S P+                  SSTSR7      oTTTTTTTT 3R lp^ RIHp T! TSR7      G Rj  x€L
  RRR4       EK2  	  EKŒ  	  S'       d    \
        P1                  R\3        S4      4       \3        S4      #   \         d"   p\
        P                  RT4        Rp?^ # Rp?ii ; i  \         d#   p\
        P                  R	Yd4        Rp?EK  Rp?ii ; i  \         d     EKÙ  i ; i Lº  + '       g   i     EKñ  ; i5i)u…  Tell the owner of each just-interrupted cron job that its run died; returns notices sent.

The cron worker can't (its thread reaches ``_deliver_result`` after teardown closed the
transport), so this runs post-interrupt while adapters are still connected. Best-effort.

Its thread reaches ``_deliver_result`` asynchronously, and by then ``_bounded_adapter_teardown`` has
closed the transport â€” so the notice never leaves the process, and ``_consume_interrupted_flag``
discards the resulting ``delivery_error`` along with it. The run's only trace is a line in jobs.json
nobody reads (#82232).
Must therefore be called from the post-interrupt phase, while adapters are still connected â€” the
same window ``_notify_active_sessions_of_shutdown`` relies on for chat sessions, which is blind to
cron work because cron runs on the scheduler's own thread pool rather than ``self._running_agents``
(#60432).
)Úget_job)Ú_resolve_delivery_targetsz+Cron interrupt notification unavailable: %sNz"gateway.shutdown.action_restartingz%gateway.shutdown.action_shutting_downT)Úfor_failurez,Cron interrupt targets unresolved for %s: %sr    z!gateway.shutdown.cron_interrupted)ÚjobÚactionr>  rì  rR   rS   ú)Cron interrupt notice to %s:%s raised: %s©rÎ  c            
   “  óš   <"  € SP                  S SSSP                  R RSR7      G Rj  x€L
 '       d   SP                  S4       R# R#  L5i)z)Cron interrupt notice to %s:%s failed: %srO  ©ÚmetadataN)Ú_send_notice_loggedr-  rì   )rÎ  rR   Ú	dedup_keyrS  r9   Únotifiedr>  r{   s   €€€€€€€€r!   Úsend_noticeÚGGatewayShutdownMixin._notify_interrupted_cron_jobs.<locals>.send_notice¿  sL   øé € Ø!%×!9Ñ!9Ø# W¨c°8·>±>ÐCnØGÐRZð ":ó "÷ ö ð %ŸL™L¨Ö3ó	ùs   ƒ&A©A	ª	A´A©Úpresent_notification©r>  z5Shutdown: delivered %d interrupted-cron-job notice(s)r   )Ú	cron.jobsrJ  r˜   rK  r<   r'   rº   r   r1   ré   r£   r   r:   Úlowerr¢   rA  rT   r-  rB   r
  r  Ú_thread_metadata_for_targetÚgateway.warning_notificationsrZ  r2   r™   )r{   Újob_idsrJ  rK  ÚerN  Újob_idrM  ÚtargetsÚjob_nameÚtargetrS   rW  rZ  rÎ  rR   rU  rS  r9   rV  r>  s   f&            @@@@@@@r!   Ú_notify_interrupted_cron_jobsÚ2GatewayShutdownMixin._notify_interrupted_cron_jobs‰  s3  ÿé € ÷ Ùð	Ý)Ý@ô ¸4×;R×;RÐ;RÑ7ÐXó  AˆÜ›ˆÜˆFð
Ù˜f“o�ßÙñ 4°CÀTÔJ�ð —w‘w˜v“×0Ð0¨&ˆHÜÐ7¸XÔUˆCØ!Ÿ-˜- R›-�ðÜ'¬¨F¯J©J°zÀ2Ó,FÓ(G×(MÑ(MÓ(OÓP�Hð Ÿ-™-×+Ñ+¨HÓ5�Ø’?¨$×*LÑ*LÈX×*VÒ*VÙÜ˜fŸj™j¨Ó3Ó4�Ø"ŸJ™J {Ó3�	Ø#Ð]Ô&8¸¿¹ÈÐR[Ó&\Ñ]�	Ø Ô(ÙÜ$¤W§]¡]Ð4_Ðai×aoÑaoÐqx×yÕyØ#×?Ñ?ÀÈ'ÐS\ÐfmÐ?Ón�H÷4ô 4õ SÙ.¨{ÀXÔN×NÐN÷ zÒyô (ñ ÷J Ü�K‰KÐOÔQTÐU]ÓQ^Ô_Ü�8‹}ÐøôY ô 	Ü�L‰LÐFÈÔJÝûð	ûô ô Ü—‘ÐKÈVÔWÞûðûô !ô Ûðúñ& O÷ z×yÑyüsÊ   ŠJ–H ¢/JÁH<Á"JÁ$
H<Á.JÂJÂJÂ'2I,Ã5JÄA?JÆ6J	ÇI?ÇJ	Ç	JÇ#*JÈH9ÈH4È.JÈ4H9È9JÈ<I)ÉI$ÉJÉ$I)É)JÉ,I<É7JÉ;I<É<JÉ?J	ÊJÊ
Jc               ó   € V ^8„  d   QhRR/# )r   r%  r:   r   )r    s   "r!   r"   rŠ   Ë  s   € ÷ oñ o¸sñ or#   c           	   ƒ  ó–  "  € ^ RI Hp Rp \        V RR4      e_   V P                  P	                  4       G Rj  x€L
  V P
                  P                  P                  V4      pV'       d   \        VRR4      MRpTf   T P                  T4      pTeC   Y3P                  P                  \        TP                  4      TP                   \        TRR4      3# T! T4      pT'       g   R# RTR,          TR,          TP                  R	4      TP                  R4      3#  Là  \         d!   p\        P                  RY4        Rp?LÈRp?ii ; i5i)
a	  ``(source, platform_str, chat_id, thread_id, profile)``: persisted origin > cached source >
parsed key. ``profile`` is the owning profile from the source or the ``agent:<profile>:`` key
namespace (``None`` = default) so the notice leaves through that profile's bot.)Ú_parse_session_keyNÚsession_storeÚoriginz>Failed to load session origin for shutdown notification %s: %srU   r>  rR   rS   )rè   rj  rH   r6  Ú_ensure_loadedrk  Ú_entriesr£   r<   r'   rº   Ú_get_cached_session_sourcer>  r-  r:   rR   rS   )r{   r%  rj  ÚsourceÚentryra  Ú_parseds   &&     r!   Ú_shutdown_notification_targetÚ2GatewayShutdownMixin._shutdown_notification_targetË  s)  é € õ 	3Øˆð	kÜ�t˜_¨dÓ3Ò?Ø×.Ñ.×=Ñ=Ó?×?Ð?Ø×*Ñ*×3Ñ3×7Ñ7¸ÓD�ß;@œ ¨°$Ô7Àd�ð Š>Ø×4Ñ4°[ÓAˆFØÒØŸ?™?×0Ñ0´#°f·n±nÓ2EÀv×GWÑGWÔY`ÐagÐirÐtxÓYyÐyÐyÙ$ [Ó1ˆßÙØ�W˜ZÕ(¨'°)Õ*<¸g¿k¹kÈ+Ó>VÐX_×XcÑXcÐdmÓXnÐnÐnñ @øô ô 	kÜ�L‰LÐYÐ[f×jÑjûð	küsK   ‚	E	Œ,D ¸D¹0D Á*D Á9A*E	Ã$5E	ÄD ÄEÄ&EÄ<E	ÅEÅE	c          
     ó,   € V ^8„  d   QhRRRRRRRRRR/# )r   rR   r:   r9   ÚkindÚplatform_strr   r   r   )r    s   "r!   r"   rŠ   á  s4   € ÷ 	ñ 	Ø #ð	Ø*-ð	Ø58ð	ØHKð	à	ñ	r#   c              ‹  ó¬   "  € VR8X  d   RMRpRV R2pV P                   ! WW5V3/ VB G Rj  x€L
 '       g   R# \        P                  RWEV4       R	#  L%5i)
zWSend one shutdown notice; True when delivered. Failures are debug-logged, never raised.úhome channelzhome channel rì  z(Failed to send shutdown notification to z	%s:%s: %sNFz&Sent shutdown notification to %s %s:%sT)rT  r'   r2   )	r{   rÎ  rR   r9   rv  rw  Úsend_kwargsÚwhereÚfail_fmts	   &&&&&&,  r!   Ú_send_shutdown_noticeÚ*GatewayShutdownMixin._send_shutdown_noticeá  s`   é € ð $(¨>Ô#9‘¸rˆØ=¸e¸WÀIÐNˆØ×-Ò-¨gÀÐS[ÑkÐ_jÑk×kÖkÙÜ�‰Ð<¸dÐRYÔZÙñ lùs   ‚*A¬A­	A·Ac               ó0   € V ^8„  d   QhRRRRRRRRRRRR	/# )
r   rR   r:   r9   rw  r|  Ú	raise_fmtrW   r   r   r   )r    s   "r!   r"   rŠ   í  s<   € ÷ ñ ØðØ$'ðØ7:ðØFIðØVcðà	ñr#   c              ‹  ód  "  € ^ RI Hp V! VP                  R4      4      VR&    V P                  ! W3/ VB G Rj  x€L
 p\        T4      '       d#   \
        P                  YCT\        T4      4       R# R#  L9  \         d-   p	\
        P                  T;'       g    TY1T	4        Rp	?	R# Rp	?	ii ; i5i)u"  ``adapter.send`` whose failure is debug-logged as ``fmt % (platform, chat, error)`` â€” ``fail_fmt``
for success=False, ``raise_fmt`` (default ``fail_fmt``) for a raise; True only on a delivered send.
Every shutdown notice races live turns, so it always carries the interim marker (#98432).)Ú_interim_metadatarS  NFT)	rè   r‚  r£   Úsendr<   r'   rº   rJ   rM   )
rÎ  rR   r9   rw  r|  r€  Úkwr‚  rD   ra  s
   &&&&&&,   r!   rT  Ú(GatewayShutdownMixin._send_notice_loggedì  s“   é € õ 	2Ù*¨2¯6©6°*Ó+=Ó>ˆˆ:‰ð	Ø"Ÿ<š<¨Ñ;¸Ñ;×;ˆFô ˜×ÒÜ�L‰L˜°¼+ÀfÓ:MÔNÙÙñ <øÜô 	Ü�L‰L˜×.Ð. h°ÀqÔIÝûð	üs=   ‚!B0¤A6 ºA4»A6 ¿5B0Á4A6 Á6B-Â!B(Â"B0Â(B-Â-B0c               ó   € V ^8„  d   QhRR/# rc  r   )r    s   "r!   r"   rŠ   ÿ  s   € ÷ kJñ kJ¸4ñ kJr#   c              ƒ  óÊ
  a aaa"  € S P                   '       d   S P                  MRp\        S P                   '       d   RMR4      oRpVeV   \        \        4      ;_uu_ 4        \        VP                  P                  VP                  VP                  4      pRRR4       \        4       oS P                  4        EFs  pS P                  V4      G Rj  x€L
 pVf   K#  Vw  rVrxp	\        WgW‰R7      oSS9   d   K?   \        V4      p
Ve   S P                  V4      MRpVf   S P!                  W©4      pV'       g   K�  S P#                  V
R4      '       g   K›  \%        VRR4      pVf   VS8X  d   \%        VRR4      pS P'                  W§V\%        VRR4      WËR7      pY·TTS3TTT 3R
 llp^ RIHp ^ RIHp Te   T! S P5                  T4      4      M	\7        4       pT;_uu_4       GRj  x€L
  T! YúTS8g  R7      G Rj  x€L
 pRRR4      GRj  x€L
  X'       d   EKb  SP9                  S4       EKv  	  S P                   '       d   Ve   \(        P+                  R4       R# \;        \<        P>                  R4      ;_uu_ 4        ^ RI H!p V! 4       '       d!   \(        PE                  R4        RRR4       R#  RRR4       \%        S RR4      ;'       g    / p\G        S PI                  4       4       EF�  w  ršpVPJ                  pV'       d   VP                  '       g   K/  V	f   S PL                  MVPO                  V	4      ;'       g    / PO                  V
4      pVf   Ko  S P#                  V
RV4      '       g   KŠ  \        V
P                  VP                  VP                  V	R7      oSS9   d   KÀ   S P'                  V
VP                  VP                  VR7      pTTY­3TTTT 3R llp^ RIHp ^ RIHp T	'       d'   \%        S RR4      ;'       g    / PO                  T	4      MRpT'       d	   T! T4      M	\7        4       ;_uu_4       GRj  x€L
  T! TT
R7      G Rj  x€L
  RRR4      GRj  x€L
  EK„  	  R#   + '       g   i     ELç; i EL¸  \         d$   p\(        P+                  R	YgT4        Rp?EKü  Rp?ii ; i EL× ELÅ EL¸  + GRj  x€L 
 '       g   i     ELÐ; i  + '       g   i     EL7; i  \         d9   p\(        P+                  RT
P                  TP                  T4        Rp?EKH  Rp?ii ; i Lõ Lå L×  + GRj  x€L 
 '       g   i     EKq  ; i5i)z¨Send shutdown/restart notifications to active chats and home channels.

Called at the start of stop() while adapters are connected; send failures never block shutdown.
Nzgateway.shutdown.notice_restartz gateway.shutdown.notice_shutdown©rU   zactive sessionÚ
message_idÚ	chat_type)rŠ  Úreply_to_message_idrÎ  z1Failed to send shutdown notification to %s:%s: %sc           	   “  ó€   <"  € SP                  WSR W#R7      G Rj  x€L
 '       d   SP                  V4       R# R#  L5i)zactive chatrR  N)r}  rì   )rÎ  rR   rw  rS  rU  r9   rV  r{   s   &&&&&€€€r!   Ú_send_activeÚNGatewayShutdownMixin._notify_active_sessions_of_shutdown.<locals>._send_active.  s?   øé € à×3Ñ3Ø c¨=¸,ð 4ó ÷ ö ð —L‘L Ö+óùs   ƒ>œ<�	>§>rY  )Ú_async_profile_runtime_scope)r>  Ú
diagnosticz@Skipping home-channel shutdown notifications for in-chat restartz.drain_notification_suppressed check failed: %s)Údrain_notification_suppressedzWHome-channel shutdown broadcast suppressed by drain marker (suppress_notification=true)rG  ry  rP  z>Failed to send shutdown notification to home channel %s:%s: %sc           	   “  ó¾   <"  € SP                  V \        VP                  4      SR VP                  VR7      G Rj  x€L
 '       d   SP	                  S4       R# R#  L5i)ry  rR  N)r}  r:   rR   r-  rì   )rÎ  Úhomer>  rS  rU  r9   rV  r{   s   &&&&€€€€r!   Ú
_send_homeÚLGatewayShutdownMixin._notify_active_sessions_of_shutdown.<locals>._send_home`  sQ   øé € Ø×3Ñ3ØœS §¡Ó.°°^ÀXÇ^Á^Ð^fð 4ó ÷ ö ð —L‘L Ö+óùs   ƒ8A»A¼	AÁAÚ_served_profile_homesr[  )(r1   Ú_restart_command_sourcer   r   r<   rT   r>  r-  rR   rS   ré   r  rs  r^   r   Ú_delivery_adapter_forÚ_authorization_adapterrF  rH   r^  r'   rº   r_  rZ  rè   r�  Ú _resolve_profile_home_for_sourcer   rì   rB   r
  r  rÝ  r‘  r2   rÎ   Ú_served_home_channel_configsÚhome_channelr¢   r£   )r{   Úrestart_sourceÚrestart_keyr%  re  rp  rw  rR   rS   rU   r>  rÎ  r‹  rS  ra  r�  rZ  r�  ÚscopeÚ	presentedr‘  rO  r@  r“  r”  Úprofile_homerU  r9   rV  s   f                         @@@r!   Ú#_notify_active_sessions_of_shutdownÚ8GatewayShutdownMixin._notify_active_sessions_of_shutdownÿ  sŒ  ûé € ð
 :>×9P×9PÐ9P˜×5Ò5ÐVZˆÜ°T×5L×5LÐ5LÑ1ÐRtÓuˆØˆØÒ%Üœ)×$Õ$Ü0Ø"×+Ñ+×1Ñ1°>×3IÑ3IÈ>×KcÑKcó�÷ %ô 9<»ˆØ×8Ñ8×:ˆKØ×=Ñ=¸kÓJ×JˆFØŠ~ÙØ@FÑ=ˆF '°gÜ,¨\ÀIÔ_ˆIØ˜HÔ$ÙðÜ# LÓ1�ð AGÒ@R˜$×4Ñ4°VÔ<ÐX\�Ø’?Ø"×9Ñ9¸(ÓL�GßÙØ×+Ñ+¨HÐ6F×GÒGÙÜ&-¨f°lÀDÓ&IÐ#Ø&Ò.°;À)Ô3KÜ*1°.À,ÐPTÓ*UÐ'Ø×;Ñ;Ø y¼GÀFÈKÐY]Ó<^Ø(;ð <ó �ð ,3ÐR^Ø,4À	÷,ñ ,õ KÝ@àÒ*ñ 2°$×2WÑ2WÐX^Ó2_Ô`Ü0;³ð ç—u’uÙ"6°|ÐcnÐr{Ñc{Ô"|×|�	÷ —uç’9Ø—‘˜Y×'ñ] ;ð^ ×"×"Ð" ~Ò'AÜ�L‰LÐ[Ô\Ùô œWŸ]™]Ð,\×]Õ]ÝKÙ,×.Ò.Ü—‘Ømôð ÷ ^Ñ]à.÷ ^ô # 4Ð)<¸dÓC×IÐIÀrÐÜ/3°D×4UÑ4UÓ4W×/XÑ+ˆG˜|Ø×,Ñ,ˆDß˜tŸ|Ÿ|˜|ÙØ(/ª�t—}’}Ð=M×=QÑ=QÐRYÓ=Z×=`Ð=`Ð^`×eÑeÐfnÓoˆGØŠÙØ×'Ñ'¨°.À,×OÒOÙÜ,¨X¯^©^¸T¿\¹\È4Ï>É>ÐcjÔkˆIØ˜HÔ$ÙðØ×;Ñ;¸HÀdÇlÁlÐTX×TbÑTbÐlsÐ;Ót�ð *1°tÀh÷ ,ò ,õ
 KÝ@ç`gœG DÐ*AÀ4ÓH×NÐNÈB×SÑSÐT[Ô\ÐmqˆLßEQÑ3°LÔAÔWbÓWd×d×dÑdÙ*¨:ÀÔI×IÐI÷ e×dÒdó9 0Y÷K %×$Ð$úò Køô4 ô Ü—‘ÐPÐR^ÐijÔkÞûðúò  Ú|÷ —u—u”uú÷ ^×]Ð]ûô4 ô Ü—‘ØTÐV^×VdÑVdÐfj×frÑfrÐtuôö ûð	úñ eÙI÷ e×d×dÔdüs›  †AU#Á#6Q7Â:U#ÃRÃ&U#Ã;>RÄ:U#Ä<RÅU#ÅARÆAU#Ç(R?
Ç)U#Ç,SÇ=S
Ç>SÈU#ÈSÈ	U#È'U#É=U#Ê*S%Ê,
U#Ê7U#ËAU#Ì)U#Í0U#Í78U#Î0)S9Ï5U#ÐU#Ð+U#Ñ	T?
Ñ
U#ÑUÑU
ÑUÑ U#Ñ+UÑ,U#Ñ7R	Ò
U#ÒR<ÒR7Ò0U#Ò7R<Ò<U#ÓSÓU#ÓS"ÓSÓ
S"ÓS"Ó	U#Ó%S6	Ó0	U#Ó9T<Ô,T7Ô0U#Ô7T<Ô<U#ÕUÕU#ÕU ÕUÕ
U ÕU Õ
U#c               ó    € V ^8„  d   QhRRRR/# ©r   rÓ   r   r   rÔ   r   )r    s   "r!   r"   rŠ   n  s   € ÷ %cñ %c°3ð %c¸4ñ %cr#   c           
     ó”  € \        \        P                  R4      ;_uu_ 4        \        V RR4      p\        V RR4      p\	        V4      '       d   \        V\        4      '       d	   V'       g    RRR4       R# \        V RR4      p\	        V4      '       d)   \        \        4      ;_uu_ 4        V! V4       RRR4        V! V4       RRR4       R#   + '       g   i     L#; i  \         dE   p\        P                  RT\        T4      4       ^ RIHp T! \        T RR4      T4        Rp?LgRp?ii ; i  + '       g   i     R# ; i)	zéPersist an in-flight transcript before teardown.

A force-interrupted agent may never reach finalize_turn (the only mid-turn flush), so its
tool rounds would vanish on resume. Idempotent; gracefully finished agents re-flush nothing.
z$Shutdown transcript flush failed: %sÚ_flush_messages_to_session_dbNÚ_session_messagesÚ)_drop_trailing_empty_response_scaffoldingz^Shutdown transcript flush failed (%s); preserving %d in-memory message(s) to recovery snapshot)Úflush_agent_history_to_fileÚ
session_id)rB   r
  r  rH   r¥   rÌ   rÎ   r   r<   r'   r«  r™   Úgateway.shutdown_flushrª  )rÓ   Ú_flushr¨  Ú_stripÚ
_flush_errrª  s   &     r!   Ú#_flush_agent_transcript_at_shutdownÚ8GatewayShutdownMixin._flush_agent_transcript_at_shutdownm  s  € ô œWŸ]™]Ð,R×SÕSô ˜UÐ$CÀTÓJˆFÜ '¨Ð/BÀDÓ IÐÜ˜V×$Ò$¬Ð4EÄt×)LÒ)L×QbØ÷ TÑSô" ˜UÐ$OÐQUÓVˆFÜ˜×ÒÜœi×(Õ(ÙÐ,Ô-÷ )ð
cÙÐ(Ô)÷- TÑS÷& )×(ûô ô cô —‘ØtØ¤Ð$5Ó 6ôõ OÙ+¬G°E¸<ÈÓ,NÐPa×bÑbûðcú÷/ T×SÐSúsT   £A	D6Á-D6Á85D6Â-	C	Â6D6Â?C$ÃC!ÃD6Ã$D3Ã/:D.Ä)D6Ä.D3Ä3D6Ä6E	c               ó    € V ^8„  d   QhRRRR/# )r   rn   zdict[str, Any]r   rÔ   r   )r    s   "r!   r"   rŠ   •  s   € ÷ vñ v¸^ð vÐPTñ vr#   c              ƒ  	ó~  "  € VP                  4        Fu  w  r#V P                  V4       V P                  \        VR R4      RRVR7      G Rj  x€L
 p V P	                  WBR7      G Rj  x€L
  V P                  VR	VR
7      G Rj  x€L
  Kw  	  R#  LA L(  \
         d    \        P                  RTRR7        LKi ; i L75i)r«  Nr  Úshutdown)r«  r>  r¶   r%  )r%  z1Session-end plugin message delivery failed for %sTr  zshutdown finalize©Úcontextr%  )	rê   r°  Ú_finalize_session_off_looprH   Ú_deliver_session_end_messagesr<   r'   rº   Ú!_cleanup_agent_resources_off_loop)r{   rn   r%  rÓ   Úmessagess   &&   r!   Ú_finalize_shutdown_agentsÚ.GatewayShutdownMixin._finalize_shutdown_agents•  sÌ   é € Ø"/×"5Ñ"5Ö"7ÑˆKØ×4Ñ4°UÔ;ð "×<Ñ<Ü" 5¨,¸Ó=È	ÐZdØ'ð =ó ÷ ˆHðnØ×8Ñ8¸Ð8Ó[×[Ð[ð
 ×8Ñ8¸ÐH[ÐitÐ8Óu×uÒuó #8ññ
 \øÜô nÜ—‘ÐPÐR]Ðhl�Ömðnúñ vùsT   ‚A
B=ÁBÁB=ÁBÁ'BÁ(BÁ,B=ÂB;ÂB=ÂBÂ#B8Â5B=Â7B8Â8B=c               ó(   € V ^8„  d   QhRRRRRRRR/# )	r   r%  rW   rÓ   r   Úexecutor_taskzOptional[Any]r   r   r   )r    s   "r!   r"   rŠ   ¦  s,   € ÷ ñ Ø(ðØ14ðØERðà	ñr#   c                ó  € Ve   Ve   VP                  4       '       d   R# \        V RR4      '       g   \        V RR4      '       d   R# V'       d8   V P                  V4      pV'       d   VP                  P                  MRVJd   R# R# )a  Emit the heartbeat only while this task still owns the live run (not after ``/new`` rebinds).

Guards against a stale ``running: delegate_task`` heartbeat outliving the run that started it: stop
once the executor finishes, the agent is gone, or the session key has been rebound to a different
live agent (e.g. the user sent ``/new`` and a fresh agent took the slot mid-run, #12029).
NFrÇ  r1   T)rÇ   rH   Ú_peek_session_stateÚturnrÓ   )r{   r%  rÓ   r¾  Ú	_hb_states   &&&& r!   Ú&_should_emit_long_running_notificationÚ;GatewayShutdownMixin._should_emit_long_running_notification¦  sr   € ð Š=˜]Ò6¸=×;MÑ;M×;OÒ;OÙô �4˜ e×,Ò,´¸Ð>RÐTY×0ZÒ0ZÙßØ×0Ñ0°Ó=ˆIß(1�	—‘×$Ò$°tÀEÓIÙÙr#   c               ó(   € V ^8„  d   QhRRRRRRRR/# )	r   rÉ   rÒ   rÓ   r   r¶  r:   r   rÔ   r   )r    s   "r!   r"   rŠ   »  s1   € ÷ Nñ N¸^ð NÐTWð NÐehð NÐmqñ Nr#   c               óä   a aaa€ R VVVV 3R llpS P                  SS4       \        S RR4      pVf   \        4       ;pS n        S P	                  V\
        P                  ! V! 4       4      4       R# )zbClean up ``agent`` only after its executor future finishes (it may still use the agent's clients).c               ó   € V ^8„  d   QhRR/# rc  r   )r    s   "r!   r"   ÚQGatewayShutdownMixin._defer_agent_cleanup_until_future_done.<locals>.__annotate__¾  s   € ÷ 	Qñ 	Q¨$ñ 	Qr#   c               “  ó4  <"  €  \         P                  ! S4      G R j  x€L
  SP                  SSR7      G R j  x€L
  R #  L!  \         P                   d     R # \         d0   p \        P                  RS'       d   RS R2MRT 4        R p ? LkR p ? ii ; i L\5i)Nz2Deferred agent worker%s finished with an error: %sú (Ú)rì  )r¶  )r£  Úshieldr¯  r<   r'   rº   r¹  )rA   rÓ   r¶  rÉ   r{   s    €€€€r!   Ú_cleanup_when_doneÚWGatewayShutdownMixin._defer_agent_cleanup_until_future_done.<locals>._cleanup_when_done¾  sŽ   øé € ð	Ü—n’n VÓ,×,Ð,ð ×8Ñ8¸ÈÐ8ÓP×PÒPñ -øÜ×)Ñ)ô ò Üô Ü—‘ØH×]dÈBÈwÈiÐWXÉ/ÐjlÐnq÷ñ ûðúñ QùsY   ƒB…A žA ŸA £B¹BºBÁ A ÁBÁBÁBÁ#BÁ$%BÂ	BÂBÂBÚ_deferred_agent_cleanup_tasksN)rà   rH   ré   rÏ  Ú_track_task_inr£  Úcreate_task)r{   rÉ   rÓ   r¶  rÍ  Útaskss   fffd  r!   Ú&_defer_agent_cleanup_until_future_doneÚ;GatewayShutdownMixin._defer_agent_cleanup_until_future_done»  sc   û€ ÷	Qó 	Qð 	×)Ñ)¨&°%Ô8Ü˜Ð=¸tÓDˆØŠ=Ü9<»Ð>ˆE�DÔ6Ø×Ñ˜E¤7×#6Ò#6Ñ7IÓ7KÓ#LÖMr#   r¶  r%  c               ó(   € V ^8„  d   QhRRRRRRRR/# )	r   rÓ   r   r¶  r:   r%  rW   r   rÔ   r   )r    s   "r!   r"   rŠ   Ñ  s2   € ÷ cñ cØðcØ&)ðcØ=Jðcà	ñcr#   c             ƒ  óJ  "  € Vf   R# VP                  R4      '       g   VR8X  d(   \        \        4      ;_uu_ 4        RVn        RRR4       V'       d   RV R2MRp \        P
                  ! V P                  V P                  V P                  V3V4      V P                  R7      G Rj  x€L
  R#   + '       g   i     Lt; i L  \         d%    \        P                  R	Y@P                  4        R# \         d"   p\        P                  R
YE4        Rp?R# Rp?ii ; i5i)u¤  Run _cleanup_agent_resources in a worker thread, bounded; on timeout the worker is left alone.

The teardown fires the memory-provider lifecycle hooks (``flush_pending`` â†’ ``on_session_end`` â†’
``shutdown`` â†’ ``close``), which read credentials/home at call time. In-turn callers carry the
profile scope through ``_run_housekeeping_in_executor``; shutdown does not (it runs on the main
loop, outside any adapter handler), so under multiplexing ``on_session_end`` failed closed and the
session tail was never committed (#110622). ``_run_release_in_profile_scope`` enters the OWNING
profile's scope from ``session_key`` when the caller has none, exactly like cache eviction.Nr´  zsession expiryFrÊ  rË  rì  ©r  zŒAgent resource cleanup%s exceeded %ss; proceeding without blocking the event loop (the worker thread is left to finish on its own). (#53175)z,Agent resource cleanup%s failed: %s (#53175))Ú
startswithr   r<   Ú_end_session_on_closer£  Úwait_forÚ_run_housekeeping_in_executorÚ_run_release_in_profile_scopeÚ_cleanup_agent_resourcesÚ_CLEANUP_TIMEOUT_SÚTimeoutErrorr'   r«  )r{   rÓ   r¶  r%  Ú	ctx_labelÚcleanup_excs   &&$$  r!   r¹  Ú6GatewayShutdownMixin._cleanup_agent_resources_off_loopÑ  sú   é € ð Š=ÙØ×Ñ˜j×)Ò)¨WÐ8HÔ-HÜœ)×$Õ$Ø.3�Ô+÷ %ç'.�b˜˜	 ‘O°Bˆ	ð	cÜ×"Ò"Ø×2Ñ2Ø×6Ñ6¸×8UÑ8UÐX]ÐW_Ðalóð ×/Ñ/ô	÷ ò ÷	 %×$úñøô ô 	Ü�N‰NðAØBK×MdÑMd÷ô ô 	cÜ�N‰NÐIÈ9×bÒbûð	cüsv   ‚D# D#¾B2ÁD#ÁD#ÁAC Â+CÂ,C Â0D#Â2C	Â=D#ÃC Ã+D Ã2D#Ã5D Ã>D Ã?DÄD#ÄD Ä D#c               ó    € V ^8„  d   QhRRRR/# r¥  r   )r    s   "r!   r"   rŠ   ñ  s   € ÷  *ñ  *¨cð  *°dñ  *r#   c           	     óŠ  € Vf   R# \        \        4      ;_uu_ 4        \        VR4      '       d»   \        VRR4      pVee   \        \        4      ;_uu_ 4        VP	                  RR7       RRR4       \        \        4      ;_uu_ 4        VP                  ^
R7       RRR4       \        VRR4      p\        V\        4      '       d   VP                  V4       MVP                  4        RRR4       \        \        4      ;_uu_ 4        \        VR4      '       d   VP                  4        RRR4       \        \        4      ;_uu_ 4        ^ R	I
Hp V! 4        RRR4       R#   + '       g   i     EL; i  + '       g   i     Lâ; i  + '       g   i     L¨; i  + '       g   i     Ly; i  + '       g   i     R# ; i)
z<Best-effort cleanup for temporary or cached agent instances.NÚshutdown_memory_providerÚ_memory_managerT)Úsession_endr×  r¨  Úclose)Úcleanup_stale_async_clients)r   r<   ÚhasattrrH   Úclaim_teardownÚflush_pendingrÌ   rÎ   rå  rè  Úagent.auxiliary_clientré  )r{   rÓ   Ú_mmÚsession_messagesré  s   &&   r!   rÝ  Ú-GatewayShutdownMixin._cleanup_agent_resourcesñ  s2  € àŠ=ÙÜ”i× Õ Ü�uÐ8×9Ò9ô ˜eÐ%6¸Ó=�Ø’?Ü!¤)×,Õ,Ø×*Ñ*°tÐ*Ô<÷ -ä!¤)×,Õ,Ø×)Ñ)°"Ð)Ô5÷ -ô $+¨5Ð2EÀtÓ#LÐ ÜÐ.´×5Ò5Ø×2Ñ2Ð3CÕDà×2Ñ2Ô4÷' !ô* ”i× Õ Ü�u˜g×&Ò&Ø—‘”÷ !ô
 ”i× Õ ÝJÙ'Ô)÷ !Ñ ÷# -×,Ð,úç,×,ú÷ !× ú÷* !× ú÷
 !× Ð úsT   ž;FÁE$	Á,FÂE8	ÂAFÄ
#FÅF1Å$E5Å/	FÅ8FÆFÆF	ÆF.	Æ1G	c               ó   € V ^8„  d   QhRR/# )r   r   r   r   )r    s   "r!   r"   rŠ     s   € ÷ 4ñ 4¨ñ 4r#   c                	ó2   € ^ RI Hp WP                  ,          # )r•   )Ú_hermes_home)rè   ró  Ú_STUCK_LOOP_FILE)r{   ró  s   & r!   Ú_stuck_loop_counts_pathÚ,GatewayShutdownMixin._stuck_loop_counts_path  s   € Ý,Ø×3Ñ3Õ3Ð3r#   c               ó    € V ^8„  d   QhRRRR/# )r   Úpathr   r   r  r   )r    s   "r!   r"   rŠ     s   € ÷ ñ  ð ¨ñ r#   c                ót   €  \         P                  ! V P                  RR7      4      #   \         d     R# i ; i)z`Parsed counter dict, or None when the file is missing/unreadable (no exists() pre-check needed).z	utf-8-sig)ÚencodingN)ÚjsonÚloadsÚ	read_textr<   )rø  s   &r!   Ú_read_json_countsÚ&GatewayShutdownMixin._read_json_counts  s3   € ð	Ü—:’:˜dŸn™n°k˜nÓBÓCÐCøÜô 	Úð	ús   ‚%( ¨7¶7c               ó    € V ^8„  d   QhRRRR/# )r   Úactive_session_keysré   r   rÔ   r   )r    s   "r!   r"   rŠ      s   € ÷ ^ñ ^ÀSð ^ÈTñ ^r#   c                ó@   € ^ RI Hp V! V P                  4       V4       R# )z^Increment persisted restart-failure counters for active sessions; drop the rest (loop broken).)Ú increment_restart_failure_countsN)Úgateway.stuck_loop_countsr  rõ  )r{   r  r  s   && r!   Ú!_increment_restart_failure_countsÚ6GatewayShutdownMixin._increment_restart_failure_counts   s   € åNÙ(¨×)EÑ)EÓ)GÐI\Ö]r#   c               ó   € V ^8„  d   QhRR/# r‰   r   )r    s   "r!   r"   rŠ   %  s   € ÷ ñ ¨cñ r#   c           	     ó†  € V P                  4       pVP                  4       '       g   ^ # V P                  V4      pVf   ^ # ^ pVP                  4        UUu. uF  w  rEWPP                  8¼  g   K  VNK  	  upp F�  p\        \        4      ;_uu_ 4        V P                  P                  P                  V4      pV'       d@   VP                  '       g.   RVn
        V^,          p\        P                  RWbV,          4       RRR4       K‘  	  V'       d;   \        \        4      ;_uu_ 4        V P                  P                  4        RRR4       \        \        4      ;_uu_ 4        VP                  RR7       RRR4       V# u uppi   + '       g   i     EK!  ; i  + '       g   i     L_; i  + '       g   i     T# ; i)zVSuspend sessions active across too many restarts (startup, AFTER crash-turn recovery).NTu_   Auto-suspended stuck session %s (active across %d consecutive restarts â€” likely a stuck loop))Ú
missing_ok)rõ  Úexistsrþ  rê   Ú_STUCK_LOOP_THRESHOLDr   r<   rk  rn  r£   Ú	suspendedr'   r«  Ú_saveÚunlink)r{   rø  r  r  ÚkÚvr%  rq  s   &       r!   Ú_suspend_stuck_loop_sessionsÚ1GatewayShutdownMixin._suspend_stuck_loop_sessions%  s@  € à×+Ñ+Ó-ˆØ�{‰{�}Š}ÙØ×'Ñ'¨Ó-ˆØŠ>ÙØˆ	Ø*0¯,©,¬.Ô\©.¡$ !¸A×A[ÑA[Ñ<[ŸA˜A©.Õ\ˆKÜœ)×$Õ$Ø×*Ñ*×3Ñ3×7Ñ7¸ÓD�ß §§ Ø&*�E”OØ •N�IÜ—N‘NØyØ#¨KÕ%8ô÷ %Ñ$ñ ]÷ Üœ)×$Õ$Ø×"Ñ"×(Ñ(Ô*÷ %ô ”i× Õ Ø�K‰K 4ˆKÔ(÷ !àÐùó! ]ß$×$Ñ$ú÷ %×$ú÷ !Ö àÐús6   ÁFÁ.FÂ?FÃ-FÄ*FÅ$F/ÆFÆF,	Æ/G 	c               ó    € V ^8„  d   QhRRRR/# )r   r%  r:   r   rÔ   r   )r    s   "r!   r"   rŠ   @  s   € ÷ Pñ P¸cð PÀdñ Pr#   c              ƒ  ó¬   "  € ^ RI Hp V P                  4       pVP                  4       '       g   R# \        P
                  ! W#V4      G Rj  x€L
  R#  L5i)zXClear a completed session's restart-failure counter off-loop (atomic_json_write fsyncs).)Úclear_restart_failure_countN)r  r  rõ  r
  r£  r¹  )r{   r%  r  rø  s   &&  r!   Ú_clear_restart_failure_countÚ1GatewayShutdownMixin._clear_restart_failure_count@  s=   é € åIØ×+Ñ+Ó-ˆØ�{‰{�}Š}Ùô ×ÒÐ ;À;ÓO×OÔOùs   ‚A	AÁAÁAc               ó   € V ^8„  d   QhRR/# ©r   r   rm   r   )r    s   "r!   r"   rŠ   L  s   € ÷ 1ñ 1 $ñ 1r#   c                 óì  € ^ RI Hp  ^ RIHpHp ^ RIHpHp V! 4       p VP                  4       V! 4       P                  4       8H  p^ RI
Hp V! 4       pRp	V'       gb   V'       gZ    ^ RIHp
 V
P                  V
P                  4      pVe4   V
P!                  V4      '       d   \#        VP$                  4      ^8”  d   Rp	V'       g2   V'       g*   V	'       g"    ^ R	IHp \+        V! 4       P,                  4      p	V'       g   V'       g	   V	'       d	   V! 4       pM
V! VRR
7      pV ! V4      pVP/                  RR4       V#   \         d    Rp ELi ; i  \         d    Rp	 L¢i ; i  \         d    Rp	 L|i ; i)uŠ  Watcher env minus ``_HERMES_GATEWAY`` (else the CLI's self-restart guard refuses; gateway stays down).

The host multiplexer is respawned with ``host_gateway_child_env`` (default-root
secrets via ``served_profile_child_env``, not ``os.environ.copy()``). A standalone
named-profile gateway keeps that profile's home â€” only a multiplexer, or a process
already on the default root, is the host.
)Údrop_bridged_env)Úget_default_hermes_rootÚget_hermes_home)Úhost_gateway_child_envÚserved_profile_child_envF)Úis_multiplex_active)Úhost_rendezvousNT)Úload_gateway_config)Útarget_homeÚinherit_credentialsÚ_HERMES_GATEWAY)Úgateway.config_loaderr  Úhermes_constantsr  r  Útools.environments.localr  r  Úresolver<   Úagent.secret_scoper   r  r!  Úread_recordÚROLE_GATEWAYÚrecord_names_live_ownerr™   ÚprofilesÚgateway.configr"  r   Úmultiplex_profilesrÚ   )r  r  r  r  r  r“  Ú
on_defaultr   Úsettled_multiplexÚ	multiplexÚhrÚrecordr"  Úwatcher_envs                 r!   Ú_restart_watcher_envÚ)GatewayShutdownMixin._restart_watcher_envK  sC  € õ 	;ßMß]áÓ ˆð	ØŸ™›Ñ+BÓ+D×+LÑ+LÓ+NÑNˆJõ 	;Ù/Ó1ÐØˆ	ß×"3ð
"Ý9ØŸ™¨¯©Ó8�ØÒ%¨"×*DÑ*DÀV×*LÒ*LÔQTÐU[×UdÑUdÓQeÐhiÔQiØ $�I÷ ×"3¿Ið"Ý>Ü Ñ!4Ó!6×!IÑ!IÓJ�	÷ ×*¯iÙ0Ó2‰Ká2Ø °dôˆKñ ' {Ó3ˆØ�‰Ð)¨4Ô0ØÐøôE ô 	Ø‹Jð	ûô$ ô "Ø!’	ð"ûô ô "Ø!’	ð"ús;   Ÿ&D< Á%;E Â!E Ã E# Ä<EÅEÅE ÅE Å#E3Å2E3c               ó(   € V ^8„  d   QhRRRRRRRR/# )	r   Ú
hermes_cmdrÎ   Úcurrent_pidr8   Úrestart_after_sra   r   rÔ   r   )r    s   "r!   r"   rŠ   €  s*   € ÷ -ñ -°4ð -Àcð -Ð\að -Ðfjñ -r#   c                óX  € ^ RI p^ RIHpHp \        P                  4       pVP                  R4      '       g,   \        P                  P                  R4      pV'       d   WvR&   \        \        4      P                  4       P                  P                  p^ RIHp	 V	! V\        V4      \        V4      .V ORNRN\         R7      p
V
^ ,          p\#        VP$                  VP$                  VR7      p VP&                  ! V
3/ VBV! 4       B  R#   \(         d”     TP&                  ! T
3/ TBR	T! 4       /B   R#   \(         de   p\+        TR
R4      p\,        P/                  R\        P0                  P3                  T4      Te   R
MRTe   TMTP4                  4        Rp? R# Rp?ii ; ii ; i)zXSpawn the detached Windows watcher (``python -c``), retrying once without job breakaway.N)Ú&windows_detach_flags_without_breakawayÚwindows_detach_popen_kwargsÚVIRTUAL_ENV)Úruntime_commandr  Úrestart)Úcode)ÚstdoutÚstderrÚenvÚcreationflagsÚwinerrorz�Detached restart watcher was not started after the no-breakaway retry (%s; %s=%r). The gateway will not be respawned by this restart attempt.Úerrno)Ú
subprocessÚhermes_cli._subprocess_compatr>  r?  re   r7  r£   ÚosÚenvironr   Ú__file__r)  ÚparentÚhermes_cli._launchersrA  r:   Ú_WINDOWS_RESTART_WATCHERrm   ÚDEVNULLÚPopenÚOSErrorrH   r'   r«  rø  ÚbasenamerI  )r:  r;  r<  rJ  r>  r?  r6  Ú	inheritedÚproject_rootrA  Úwatcher_argvÚwatcher_pythonÚpopen_kwargsrA   rH  s   &&&            r!   Ú_spawn_windows_restart_watcherÚ3GatewayShutdownMixin._spawn_windows_restart_watcher  s~  € ó 	÷	
ô +×?Ñ?ÓAˆð �‰˜}×-Ò-ÜŸ
™
Ÿ™ }Ó5ˆIßØ-6˜MÑ*ÜœH“~×-Ñ-Ó/×6Ñ6×=Ñ=ˆõ 	:Ù& |Ü�Óœs ?Ó3ÐW°jÐWÀ)ÐWÈYÐWÜ)ô+ˆð & a�ˆÜ :×#5Ñ#5¸j×>PÑ>PÐVaÔbˆð	Ø×Ò˜\Ñ[¨\Ð[Ñ=XÓ=ZÔ[øÜô 	ðØ× Ò Ø ñØ$0ñÙ@fÓ@höøô ô 
ô # 3¨
°DÓ9�Ü—‘ð<ä=?¿W¹W×=MÑ=MÈnÓ=]Ø"*Ò"6‘J¸GØ (Ò 4‘H¸#¿)¹)÷ó ûð	
úð	ús1   Ã/D ÄF)ÄD6Ä6F%ÅAF ÆF)Æ F%Æ%F)c               ó   € V ^8„  d   QhRR/# rc  r   )r    s   "r!   r"   rŠ   ¯  s   € ÷ 
ñ 
¸ñ 
r#   c           	   ƒ  	óª  "  € ^ RI Hp ^ RIp^ RIpV! 4       pV'       g   \        P                  R4       R# V P                  '       d   R# RV n        \        P                  ! 4       p\        \        \        V RR4      ;'       g    R4      R,           R4      p\        P                  R8X  d   \        P                  WEV4       R# R	P!                  R
 V 4       4      pR\#        V4       RV RV R2pVP%                  R4      p	V	'       d   V	RRV.MRRV.p
VP'                  W£P(                  VP(                  \        P+                  4       RR7       R# 5i)r•   )Ú_resolve_hermes_binNz4Could not locate hermes binary for detached /restartTÚ_restart_drain_timeoutrp   ç      @Úwin32Ú c              3  óN   "  € T F  p\         P                  ! V4      x € K  	  R # 5irw   )ÚshlexÚquote)rÈ   Úparts   & r!   rÊ   ÚHGatewayShutdownMixin._launch_detached_restart_command.<locals>.<genexpr>¿  s   é € Ð@±Z¨T”u—{’{ 4×(Ð(³Zùrž  zdeadline=$(( $(date +%s) + z )); while kill -0 zC 2>/dev/null && [ $(date +%s) -lt $deadline ]; do sleep 0.2; done; z gateway restartÚsetsidÚbashz-lc)rD  rE  rF  Ústart_new_session)rè   r_  ÚshutilrJ  r'   r(   Ú _detached_restart_helper_startedrL  Úgetpidr¦   ra   rH   Úsysr>  re   r[  r¨  r8   ÚwhichrS  rR  r7  )r{   r_  rl  rJ  r:  r;  r<  ÚcmdÚ	shell_cmdÚ
setsid_binÚargvs   &          r!   Ú _launch_detached_restart_commandÚ5GatewayShutdownMixin._launch_detached_restart_command¯  s4  é € Ý3ÛÛÙ(Ó*ˆ
ßÜ�L‰LÐOÔPÙØ×0×0Ð0ÙØ04ˆÔ-Ü—i’i“kˆÜœe¤G¨DÐ2JÈCÓ$P×$WÐ$WÐTWÓXÐ[^Õ^Ð`cÓdˆÜ�<‰<˜7Ô"Ü ×?Ñ?À
ÐYhÔiÙØ�h‰hÑ@±ZÓ@Ó@ˆà)¬#¨oÓ*>Ð)?ð @Ø(˜MÐ)lØˆeÐ#ð%ð 	ð
 —\‘\ (Ó+ˆ
ß9C�
˜F E¨9Ñ5È&ÐRWÐYbÐIcˆØ×ÑØ×+Ñ+°J×4FÑ4FÜ$×9Ñ9Ó;Ètð 	ö 	
ùs   ‚AEÁ	D
Ec               ó   € V ^8„  d   QhRR/# r‰   r   )r    s   "r!   r"   rŠ   Ì  s   € ÷ Oñ O Sñ Or#   c                óL   € V P                  4       V P                  4       ,           # )zûWork units the restart wait may skip: chat agents idle past ``agent.gateway_timeout`` and
cron runs older than the scheduler's stale-inflight allowance (#115469).

API work has no activity clock and pending sentinels are brand-new, so neither counts.
)Ú_wedged_chat_agent_countÚ_wedged_cron_job_countrz   s   &r!   Ú_wedged_agent_countÚ(GatewayShutdownMixin._wedged_agent_countÌ  s!   € ð ×,Ñ,Ó.°×1LÑ1LÓ1NÕNÐNr#   c               ó   € V ^8„  d   QhRR/# r  r   )r    s   "r!   r"   rŠ   Ô  s   € ÷ 
`ñ 
`¨4ñ 
`r#   c                ól   €  ^ RI Hp V! 4       #   \         d    RT P                  4       R^ R^ /u # i ; i)zÇ``cron.scheduler.get_restart_wait_cron_counts``, counted per profile-scoped run.

Fail-soft toward waiting: if the split can't be read, every active cron run stays
awaitable and nothing is excluded.
)Úget_restart_wait_cron_countsÚ	awaitableÚwedgedÚrestart_safe)r˜   r  r<   rŽ   )r{   r  s   & r!   Ú_restart_wait_cron_countsÚ.GatewayShutdownMixin._restart_wait_cron_countsÔ  sB   € ð	`ÝCÙ/Ó1Ð1øÜô 	`Ø ×!<Ñ!<Ó!>ÀÈ!È^Ð]^Ð_Ò_ð	`ús   ‚ �!3²3c               ó   € V ^8„  d   QhRR/# r‰   r   )r    s   "r!   r"   rŠ   à  s   € ÷ :ñ :¨ñ :r#   c                ó0   € V P                  4       R,          # )zYCron runs past ``cron.scheduler.get_wedged_job_ids``'s allowance; 0 if cron can't import.r�  ©rƒ  rz   s   &r!   rz  Ú+GatewayShutdownMixin._wedged_cron_job_countà  s   € à×-Ñ-Ó/°Õ9Ð9r#   c               ó   € V ^8„  d   QhRR/# r‰   r   )r    s   "r!   r"   rŠ   ä  s   € ÷ @ñ @¨#ñ @r#   c                ó0   € V P                  4       R,          # )uš  Cron runs whose worker owns a restart-safe systemd scope; 0 if cron can't import.

Such a worker runs outside the gateway cgroup, so neither the tool-process sweep nor
``mark_running_jobs_interrupted`` reaches it, and its final send rides the durable delivery
queue for whichever gateway is live next. The restart after-turn wait must therefore not
hold the gateway in ``draining`` for it: waiting buys the run nothing and refuses new turns
for up to the whole cap (observed live: a 100-minute job held the gateway ~30 minutes).
Degraded (no user bus) workers are NOT in this set â€” they share the cgroup and die with a
systemd stop, so they keep holding the wait.
r‚  r‡  rz   s   &r!   Ú_restart_safe_cron_countÚ-GatewayShutdownMixin._restart_safe_cron_countä  s   € ð ×-Ñ-Ó/°Õ?Ð?r#   c               ó   € V ^8„  d   QhRR/# r‰   r   )r    s   "r!   r"   rŠ   ñ  s   € ÷ 
ñ 
¨#ñ 
r#   c           
     óÒ   aaaa€ ^ RI HoHp V! RR4      oS^ 8:  d   ^ # R R lo\        VVVV3R l\	        \        V RR4      ;'       g    / P                  4       4       4       4      # )	z‰Running chat agents with no activity for ``agent.gateway_timeout`` (0 when disabled);
an unreadable activity summary means "not wedged".
)r$  Ú
_float_envÚHERMES_AGENT_TIMEOUTi  c               ó    € V ^8„  d   QhRRRR/# )r   rÓ   r   r   rj   r   )r    s   "r!   r"   ÚCGatewayShutdownMixin._wedged_chat_agent_count.<locals>.__annotate__ú  s   € ÷ 	ñ 	 ð 	¨ñ 	r#   c                óÜ   € \        V R R4      p\        V4      '       g   R#  V! 4       p\        V\        4      '       d   \	        VP                  RR4      4      # R#   \         d     R# i ; i)Úget_activity_summaryNÚseconds_since_activityrp   )rH   r¥   rÌ   rm   ra   r£   r<   )rÓ   Ú
summary_fnÚsummarys   &  r!   Ú_idle_secondsÚDGatewayShutdownMixin._wedged_chat_agent_count.<locals>._idle_secondsú  sh   € Ü  Ð(>ÀÓEˆJÜ˜J×'Ò'ÙðÙ$›,�ÜLVÐW^Ô`d×LeÒLe”u˜WŸ[™[Ð)AÀ3ÓGÓHÐoÐkoÐoøÜô Úðús   ¢7A ÁA ÁA+Á*A+c              3  ór   <"  € T F,  pVf   K	  VSJg   K  S! V4      ;of   K  SS8¼  g   K(  ^x € K.  	  R # 5irw   r   )rÈ   rÓ   r$  r˜  Úidler  s   & €€€€r!   rÊ   Ú@GatewayShutdownMixin._wedged_chat_agent_count.<locals>.<genexpr>  sO   øé € ð 
áV�Øô à%*Ð2IÐ%Iô ñ ' uÓ-Ð-�ô ð @DÀw¹÷ ŠAÛVùs   ƒ7Ž7–	7¤7­
7r  N)rè   r$  r�  rÍ   rÎ   rH   rM  )r{   r�  r$  r˜  r›  r  s   & @@@@r!   ry  Ú-GatewayShutdownMixin._wedged_chat_agent_countñ  sa   û€ ÷ 	DÙÐ3°TÓ:ˆØ�aŒ<Ùõ	ô ÷ 
äœw tÐ->ÀÓE×KÐKÈ×SÑSÓUÔVó
ó 
ð 	
r#   c               ó   € V ^8„  d   QhRR/# r‰   r   )r    s   "r!   r"   rŠ     s   € ÷ 
ñ 
 sñ 
r#   c                ó  € V P                  4       \        V 4      ,           V P                  4       ,           V P                  4       ,           p\	        ^ WP                  4       ,
          4      V P                  4       R,          ,           # )u  Active work minus the units the restart wait must not hold for.

Two disjoint exclusions: wedged turns (idle past ``agent.gateway_timeout`` / past the cron
in-flight allowance â€” restart is their remedy, #115469) and cron runs executing in a
restart-safe external worker (``_restart_safe_cron_count``), which outlives this process
either way. Cron runs are counted per profile-scoped run by the scheduler, not by
subtracting from the bare-ID active count: two profiles can run the same job ID, and one
excluded run must not hide the other.
r€  )r�   r   r�   r�   r¦   ry  rƒ  )r{   Únon_crons   & r!   Ú_awaitable_work_countÚ*GatewayShutdownMixin._awaitable_work_count  sy   € ð ×%Ñ%Ó'Ü  Ó&õ'à×(Ñ(Ó*õ+ð ×6Ñ6Ó8õ9ð 	ô ��8×;Ñ;Ó=Õ=Ó>Ø×,Ñ,Ó.¨{Õ;õ<ð	
r#   c               ó   € V ^8„  d   QhRR/# r;  r   )r    s   "r!   r"   rŠ      s   € ÷ &ñ & tñ &r#   c                óN  a€ ^ RI Hp \        P                  ! 4       p. p\        V P	                  4       P                  4       4       EF  w  rEVP                  P                  pVf   K"  RRRVR\        P                  ! 4       /pVP                  P                  '       d*   \        W%P                  P                  ,
          ^4      VR&   WaJd~   \        VRR4      VR&   \        VR	R4      p\        V4      '       dP   \        \        4      ;_uu_ 4        V! 4       p	V	P!                  R
4      VR
&   V	P!                  R4      VR&   RRR4       VP#                  V4       EK  	  \        \        4      ;_uu_ 4        ^ RIHp
Hp V! 4       pV
! 4        F†  pTP#                  RRRVR,          RVR,          RVR,          ;'       g    \        P                  ! 4       R\+        VR,          4      RVR,          V9   R\+        VP!                  R4      4      /4       Kˆ  	  RRR4       R\-        V 4      3RV P/                  4       3RV P1                  4       33 F*  w  opVP3                  V3R l\5        V4       4       4       K,  	  V#   + '       g   i     ELG; i  + '       g   i     L„; i)uÉ  One dict per in-flight work unit the restart wait is holding for, so an observer
(``hermes update``, ``hermes gateway status``) can name it instead of printing a bare count.

``kind`` âˆˆ ``chat`` (session turn), ``cron`` (job id + external worker pid when the run was
handed to a restart-safe scope), ``managed`` / ``api`` / ``deferred`` (count only â€” those sources expose
no identity). Best-effort: a source that can't be read is omitted, never raises.
r3  Nrv  ÚchatÚsessionÚpidÚ	elapsed_sÚmodelr”  Úcurrent_toolr•  Úidle_s)Úget_running_job_detailsÚget_wedged_job_idsr  rb  Ú
worker_pidÚexternalr�  r‚  Úmanagedr  r  c              3  óV   <"  € T F  pR SR\         P                  ! 4       /x € K   	  R# 5i)rv  r§  N)rL  rn  )rÈ   Ú_rv  s   & €r!   rÊ   Ú=GatewayShutdownMixin._describe_active_work.<locals>.<genexpr>E  s    øé € ÐRÁ\À˜& $¨¬r¯yªy«{Õ;Ã\ùs   ƒ&))rè   r$  rx   rÎ   Ú_sessions_maprê   rÁ  rÓ   rL  rn  Ú
started_tsÚroundrH   r¥   r   r<   r£   rA  r˜   r¬  r­  r   r   r�   r�   ÚextendÚrange)r{   r$  r
  Úunitsr(  r�  rÓ   Úunitr–  r—  r¬  r­  r�  rM  r&  rv  s   &              @r!   Ú_describe_active_workÚ*GatewayShutdownMixin._describe_active_work   s(  ø€ õ 	8Ü�iŠi‹kˆØˆÜ˜t×1Ñ1Ó3×9Ñ9Ó;×<‰JˆCØ—J‘J×$Ñ$ˆEØŠ}ÙØ  &¨)°S¸%ÄÇÂÃÐMˆDØ�z‰z×$×$Ð$Ü$)¨#·
±
×0EÑ0EÕ*EÀqÓ$I��[Ñ!ØÓ3Ü '¨¨w¸Ó =��W‘Ü$ UÐ,BÀDÓI�
Ü˜J×'Ò'Ü!¤)×,Õ,Ù",£,˜Ø/6¯{©{¸>Ó/J˜˜^Ñ,Ø)0¯©Ð5MÓ)N˜˜X™÷ -ð �L‰L˜×ñ =ô  ”i× Õ ßRÙ'Ó)ˆFÙ.Ö0�Ø—‘˜f f¨h¸¸H½À{ÐTWÐXcÕTdØ# S¨Õ%6×%EÐ%E¼"¿)º)»+ÀzÔSWÐX[Ð\hÕXiÓSjØ&¨¨H­¸Ñ(?Ø,¬d°3·7±7¸>Ó3JÓ.KðMö Nñ 1÷ !ð 'Ô(:¸4Ó(@ÐAÀEÈ4×KeÑKeÓKgÐChØ'¨×)QÑ)QÓ)SÐTóV‰KˆD�%à�L‰LÔRÄUÈ5Ä\ÓRÖRñVð ˆ÷! -×,Ð,ú÷
 !× ús    Ä0J Å7A	JÇAJÊ JÊJ$	c               ó   € V ^8„  d   QhRR/# r   r   )r    s   "r!   r"   rŠ   H  s   € ÷ @ñ @¸ñ @r#   c           	   ƒ  ó´  "  € V P                  4       pV^ 8:  d   R# V P                  4       ^ 8:  d7   \        P                  RVV P	                  4       V P                  4       4       R# \        \        V RR4      ;'       g    R4      pV^ 8:  d   \        P                  RV4       R# \        P                  RW4       V P                  RR	4       \        P                  ! 4       pVP                  4       V,           pRpV P                  4       ^ 8”  dÖ   VP                  4       pWd8¼  d'   \        P                  R
W P                  4       4       R# We,
          R8¼  dm   \        P                  RV P                  4       V P	                  4       V P                  4       WF,
          V P                  4       4       V P                  RR	4       Tp\        P                  ! R4      G Rj  x€L
  Kê  V P                  4       ^ 8”  dE   \        P                  RV P                  4       V P	                  4       V P                  4       4       R# \        P                  R4       R#  Lv5i)a  Wait for in-flight work before ``stop()`` so the requesting turn isn't force-interrupted.

Wedged turns are excluded (restart is their remedy). True when drained to zero, False when the
cap elapsed or only wedged work remains (caller proceeds to ``stop()``).
TzæRestart requested with %d active work unit(s), none awaitable (%d wedged past the inactivity timeout, %d in restart-safe external cron workers that outlive this process); skipping the after-turn wait and proceeding to stop()/drainFÚ_restart_after_turn_timeoutrp   uq   Restart requested with %d active work unit(s); restart_after_turn_timeout=0 â€” entering stop()/drain immediatelyz‹Restart requested with %d active work unit(s); deferring stop() until they finish (cap=%.0fs) so in-flight turns are not amputated (#77184)r¡  z restart wait: status mark failedzŠRestart after-turn wait timed out after %.0fs with %d still active; proceeding to stop()/drain which may interrupt remaining work (#77184)rš  z’Restart deferred: waiting on %d active work unit(s) (%d wedged and excluded, %d restart-safe and excluded; %.0fs remaining before force drain): %sr  Nz}Restart deferred wait: %d excluded work unit(s) remain (%d wedged, %d restart-safe external cron); proceeding to stop()/drainuL   Restart deferred wait complete â€” active work drained; proceeding to stop())r‘   r¡  r'   r«  r{  r‹  ra   rH   r2   rˆ  r£  r  rx   r»  r¤  )r{   rB  r  r  rÃ  r  r
  s   &      r!   Ú!_await_active_work_before_restartÚ6GatewayShutdownMixin._await_active_work_before_restartH  s  é € ð ×(Ñ(Ó*ˆØ�QŒ;ÙØ×%Ñ%Ó'¨1Ô,Ü�N‰Nð-ð /5Ø×(Ñ(Ó*¨D×,IÑ,IÓ,Kôñ Üœ Ð&CÀSÓI×PÐPÈSÓQˆØ�aŒ<Ü�K‰KðUØV\ôñ Ü�‰ð/à06ô	
ð
 	×"Ñ" :Ð/QÔRÜ×'Ò'Ó)ˆØ—9‘9“; Õ(ˆØˆØ×(Ñ(Ó*¨QÔ.Ø—)‘)“+ˆCØŒÜ—‘ð8à9@×BYÑBYÓB[ôñ
 ØÕ$¨Ô-Ü—‘ð>ð ×.Ñ.Ó0°$×2JÑ2JÓ2LØ×1Ñ1Ó3°Xµ^Ø×.Ñ.Ó0ôð ×*Ñ*¨:Ð7YÔZØ!$�Ü—-’- Ó$×$Ò$Ø×"Ñ"Ó$ qÔ(Ü�N‰NðYà×'Ñ'Ó)¨4×+CÑ+CÓ+EØ×-Ñ-Ó/ô	ñ Ü�‰ÐbÔcÙñ %ùs   ‚A=IÂ EIÇIÇ A7IÚdetachedFÚvia_servicec               ó$   € V ^8„  d   QhRRRRRR/# )r   rÂ  r   rÃ  r   r   )r    s   "r!   r"   rŠ   Š  s"   € ÷  ñ  ¨4ð  Àdð  ÐW[ñ  r#   c               	ó  a aaa€ S P                   '       d   R # RS n        SS n        SS n        RS n         RS n        ^ RIHp V! S 4       S P                  4        R VV V3R llo\        4       P                  V3R l4      S n
        R# )FT)Úopen_restart_waitc               ó   € V ^8„  d   QhRR/# rc  r   )r    s   "r!   r"   Ú:GatewayShutdownMixin.request_restart.<locals>.__annotate__™  s   € ÷ 	bñ 	b Dñ 	br#   c               “  ó€  <"  € SP                  4       G R j  x€L
  S '       dD   \        \        P                  R4      ;_uu_ 4        SP	                  4       G R j  x€L
  R R R 4       \
        P                  ! R4      G R j  x€L
  SP                  RS SR7      G R j  x€L
  R #  L‹ LJ  + '       g   i     LP; i L9 L5i)Nz4Failed to launch detached gateway restart helper: %sgš™™™™™©?T)rB  Údetached_restartÚservice_restart)rÀ  rB   r
  ÚERRORru  r£  r¤  Ústop)rÂ  r{   rÃ  s   €€€r!   Ú_run_restartÚ:GatewayShutdownMixin.request_restart.<locals>._run_restart™  s‹   øé € Ø×8Ñ8Ó:×:Ð:çÜ$¤W§]¡]Ð4j×kÕkØ×?Ñ?ÓA×AÐA÷ lä—-’- Ó%×%Ð%Ø—)‘) D¸8ÐU`�)Óa×aÒañ ;ñ B÷ l×kúá%Ùaùs\   ƒB>—B#˜B>¤"B>ÁB'ÁB%ÁB'Á!B>Â B:ÂB>ÂB<ÂB>Â%B'Â'B7	Â2	B>Â<B>c                 ó:   <€ \         P                  ! S ! 4       4      # rw   )r£  rÑ  )rÎ  s   €r!   rw  Ú6GatewayShutdownMixin.request_restart.<locals>.<lambda>©  s   ø€ ´7×3FÒ3FÁ|Ã~Ô3Vr#   )Ú_restart_task_startedr1   Ú_restart_detachedr3   rÇ  rÝ  rÆ  rÀ   r   ÚrunÚ_restart_task)r{   rÂ  rÃ  rÆ  rÎ  s   fdd @r!   Úrequest_restartÚ$GatewayShutdownMixin.request_restartŠ  sw   û€ Ø×%×%Ð%ÙØ"&ˆÔØ!)ˆÔØ$/ˆÔ!Ø%)ˆÔ"àˆŒÝ;Ù˜$Ôð 	×.Ñ.Ô0÷	bò 	bô  %›YŸ]™]Ô+VÓWˆÔÙr#   c               ó   € V ^8„  d   QhRR/# r   r   )r    s   "r!   r"   rŠ   ¬  s   € ÷ ñ ¨ñ r#   c                ó   € V P                   '       d   V P                  P                  ^ 8:  d   R# V P                  e   R# ^ RIHp V! RR7      pVP                  4       '       g   R# W n        VP                  R4       R# )zAStart sd_notify only after a configured gateway is truly running.FT)ÚSystemdWatchdog)Úconfig_enabledzHermes Gateway running)r¦  rK  Úsystemd_watchdog_secondsÚ_systemd_watchdogÚgateway.systemd_notifyrÚ  ÚstartÚready)r{   rÚ  Úwatchdogs   &  r!   Ú_start_systemd_watchdogÚ,GatewayShutdownMixin._start_systemd_watchdog¬  sa   € à�}�}ˆ} §¡× DÑ DÈÔ IÙØ×!Ñ!Ò-ÙÝ:Ù"°$Ô7ˆØ�~‰~×ÒÙØ!)ÔØ�‰Ð/Ô0Ùr#   c               ó   € V ^8„  d   QhRR/# rc  r   )r    s   "r!   r"   rŠ   º  s   € ÷ ñ ¨dñ r#   c              ƒ  ót   "  € V P                   pVf   R# RV n         VP                  4       G Rj  x€L
  R#  L5i)z;Stop heartbeats before any potentially long shutdown drain.N)rÝ  rÍ  )r{   rá  s   & r!   Ú_stop_systemd_watchdogÚ+GatewayShutdownMixin._stop_systemd_watchdogº  s0   é € à×)Ñ)ˆØÒÙØ!%ˆÔØ�m‰m‹o×Ôùs   ‚-8¯6°8c               ó$   € V ^8„  d   QhRRRRRR/# ©r   r  r:   rn  ro  r   r   r   )r    s   "r!   r"   rŠ   Å  s"   € ÷ ñ ˜3ð Ð$5ð ¸#ñ r#   c                óp   €  V! 4       #   \          d"   p\        P                  RY4        Rp?R# Rp?ii ; i)zURun one best-effort teardown step; a failure is debug-logged as ``"<label>: <exc>"``.z%s: %sNrs  )r  rn  Ú_es   && r!   Ú_quiet_stepÚ GatewayShutdownMixin._quiet_stepÄ  s/   € ð	Ù“4ˆKøÜô 	Ü�L‰L˜ 5Ô-Ýûð	ús   ‚	 ‰5”0°5c               ó    € V ^8„  d   QhRRRR/# ©r   Úphaser:   r   rÎ   r   )r    s   "r!   r"   rŠ   Î  s   € ÷ <!ñ <!¨Cð <!°Dñ <!r#   c                óì   a a€ R V 3R llpR V 3R lloR V3R llpR V 3R llpR	 VV 3R
 llpV! RV4       V! RV4      ;'       g    . pV! RV4       R R lpR R lpV! RV4       V! RV4       V# )a   Kill tool subprocesses + terminal envs + browsers; returns cron job IDs marked interrupted.

Called twice: after a drain timeout (reclaim children before systemd SIGKILLs) and as a final
catch-all. Best-effort; one failing subsystem cannot block the rest.
c               ó$   € V ^8„  d   QhRRRRRR/# ré  r   )r    s   "r!   r"   ÚGGatewayShutdownMixin._stop_kill_tool_subprocesses.<locals>.__annotate__Õ  s'   € ÷ 	Tñ 	T˜ð 	TÐ"3ð 	T¸ñ 	Tr#   c                ó<   <€ \         P                  V  R S R2V4      # )rÊ  z) error)re   rì  )r  rn  rð  s   &&€r!   Ú_stepÚ@GatewayShutdownMixin._stop_kill_tool_subprocesses.<locals>._stepÕ  s$   ø€ Ü'×3Ñ3°u°g¸RÀ¸wÀgÐ4NÐPRÓSÐSr#   c               ó$   € V ^8„  d   QhRRRRRR/# )r   Úfmtr:   rn  rh   r   rÔ   r   )r    s   "r!   r"   ró  Ø  s"   € ÷ 	+ñ 	+˜Sð 	+Ð&7ð 	+¸Dñ 	+r#   c                óX   <€ V! 4       pV'       d   \         P                  V SV4       R # R # rw   )r'   r2   )rø  rn  Únrð  s   && €r!   Ú_count_stepÚFGatewayShutdownMixin._stop_kill_tool_subprocesses.<locals>._count_stepØ  s"   ø€ Ù“ˆAßÜ—‘˜C ¨Ö*ñ r#   c               ó   € V ^8„  d   QhRR/# rc  r   )r    s   "r!   r"   ró  Ý  s   € ÷ 	Nñ 	N ñ 	Nr#   c                 ó0   <a € ^ RI Ho  S! RV 3R l4       R# )r•   r   z,Shutdown (%s): killed %d tool subprocess(es)c                 ó(   <€ S P                  R R7      # )Úgateway_shutdown)rp  )Úkill_allr   s   €r!   rw  Ú\GatewayShutdownMixin._stop_kill_tool_subprocesses.<locals>._kill_processes.<locals>.<lambda>ã  s   ø€ Ð(×1Ñ1Ð9KÐ1ÔLr#   N)r  r  )r  rû  s   @€r!   Ú_kill_processesÚJGatewayShutdownMixin._stop_kill_tool_subprocesses.<locals>._kill_processesÝ  s   ù€ Ý?ñ Ø>ÜLöNr#   c               ó   € V ^8„  d   QhRR/# r;  r   )r    s   "r!   r"   ró  å  s   € ÷ 	 ñ 	 ¨ñ 	 r#   c                 óœ   <€ ^ RI Hp  V ! RS R24      pV'       d1   \        P                  RS\	        V4      RP                  V4      4       V# )r•   )Úmark_running_jobs_interruptedzGateway shutdown (z;) killed the job's tool subprocess before the run finished.z>Shutdown (%s): marked %d in-flight cron job(s) interrupted: %sr›  )r˜   r  r'   r«  r™   r¨  )r  Ú_interruptedrð  s     €r!   Ú_mark_cron_interruptedÚQGatewayShutdownMixin._stop_kill_tool_subprocesses.<locals>._mark_cron_interruptedå  sQ   ø€ õ EÙ8Ø$ U GÐ+fÐgóˆL÷ Ü—‘ØTØœ3˜|Ó,¨d¯i©i¸Ó.Eôð  Ðr#   c               ó   € V ^8„  d   QhRR/# rc  r   )r    s   "r!   r"   ró  ö  s   € ÷ 	ñ 	¨ñ 	r#   c                 ó2   <a € ^ RI Ho  S! RV V3R l4       R# )r•   )Úinterrupt_allz6Shutdown (%s): interrupted %d background delegation(s)c                 ó   <€ S ! R S R2R7      # )zgateway shutdown (rË  ©r¶   r   )Ú_interrupt_asyncrð  s   €€r!   rw  ÚcGatewayShutdownMixin._stop_kill_tool_subprocesses.<locals>._interrupt_delegations.<locals>.<lambda>ú  s   ø€ Ñ(Ð2DÀUÀGÈ1Ð0MÕNr#   N)rû   r  )r  rû  rð  s   @€€r!   Ú_interrupt_delegationsÚQGatewayShutdownMixin._stop_kill_tool_subprocesses.<locals>._interrupt_delegationsö  s   ù€ ÝPÙØHÝNör#   zprocess_registry.kill_allr  zasync interrupt_allc               ó   € V ^8„  d   QhRR/# rc  r   )r    s   "r!   r"   ró     s   € ÷ 	'ñ 	' tñ 	'r#   c                 ó    € ^ RI Hp  V ! 4        R# )r•   ©Úcleanup_all_environmentsN)Útools.terminal_tool_lifecycler  r  s    r!   Ú_cleanup_environmentsÚPGatewayShutdownMixin._stop_kill_tool_subprocesses.<locals>._cleanup_environments   s   € ÝNÙ$Ö&r#   c               ó   € V ^8„  d   QhRR/# rc  r   )r    s   "r!   r"   ró    s   € ÷ 	#ñ 	# 4ñ 	#r#   c                 ó    € ^ RI Hp  V ! 4        R# )r•   ©Úcleanup_all_browsersN)Útools.browser_tool_lifecycler  r  s    r!   Ú_cleanup_browsersÚLGatewayShutdownMixin._stop_kill_tool_subprocesses.<locals>._cleanup_browsers  s   € ÝIÙ Ö"r#   r  r  r   )	rð  rõ  r  r	  r  Ú_marked_cron_jobsr  r   rû  s	   f       @r!   Ú_stop_kill_tool_subprocessesÚ1GatewayShutdownMixin._stop_kill_tool_subprocessesÍ  s”   ù€ ÷	Tð 	T÷	+ð 	+÷
	Nð 	N÷	 ð 	 ÷"	ñ 	ñ 	Ð)¨?Ô;Ù!Ð"AÐCYÓZ×`Ð`Ð^`ÐÙÐ#Ð%;Ô<õ	'õ	#ñ 	Ð(Ð*?Ô@ÙÐ$Ð&7Ô8Ø Ð r#   c               ó    € V ^8„  d   QhRRRR/# rï  r   )r    s   "r!   r"   rŠ     s   € ÷ 
ñ 
¸3ð 
À4ñ 
r#   c              ƒ  óh   "  € \         P                  ! \        P                  V 4      G Rj  x€L
 #  L5i)u  Run _stop_kill_tool_subprocesses in a worker thread; returns cron job IDs marked interrupted.

``kill_all`` fans out into per-target ``kill_process`` calls that do blocking work
(registry checkpoint disk I/O, ``subprocess.run`` for systemd scopes, sandbox exec),
so running the sweep inline would monopolize the gateway event loop (#116327).
Offloaded with ``asyncio.to_thread`` â€” the loop's default executor, deliberately NOT
the gateway-owned ``self._executor``, which ``_stop_quiesce_and_close_session_dbs``
drains right after this phase. Phase order is preserved: callers await this before
cron notices / adapter teardown. If the surrounding stop task is cancelled while the
worker runs, the thread is left to finish on its own; the thread-based shutdown
watchdog remains the hard backstop.
N)r£  r¹  re   r#  )rð  s   &r!   Ú%_stop_kill_tool_subprocesses_off_loopÚ:GatewayShutdownMixin._stop_kill_tool_subprocesses_off_loop  s/   é € ô ×&Ò&Ü ×=Ñ=¸uó
÷ 
ð 	
ñ 
ùs   ‚)2«0¬2c               ó    € V ^8„  d   QhRRRR/# ©r   Úctxrg   r   rÔ   r   )r    s   "r!   r"   rŠ     s    € ÷ \ñ \Ð.Oð \ÐTXñ \r#   c              ƒ  ó  "  € \         P                  RV P                  '       d   RMR4       \        P                  ! 4       Vn        RV n        V P                  4        RV n        ^ RI	H
p V! V 4      G Rj  x€L
  V P                  4        \        V RR4      p\        V4      '       d.    V! R	R
7      G Rj  x€L
 '       g   \         P                  R4       \        V RR4      p\        V4      '       d   V! 4       G Rj  x€L
  V P!                  4       G Rj  x€L
  V P#                  4       G Rj  x€L
  \         P                  RVP%                  4       4       R#  Lâ L¤  \         d    \         P                  RRR7        L«i ; i L† Lp LZ5i)z:Flag teardown, stop room worker/watchdog, notify sessions.zStopping gateway%s...z for restartrì  FT)Údrain_gateway_runtimeNÚ_stop_hosted_room_workerra  r×  zXGroup Chat worker is still settling durable work; the next gateway start will recover itzZGroup Chat worker could not stop cleanly; the next gateway start will recover durable workr  ræ  z5Shutdown phase: notify_active_sessions done at +%.2fs)r'   r2   r1   rx   ry   rk   r¦  Ú_clear_plugin_message_injectorrÇ  Úgateway.run_runtimer-  rÀ   rH   r¥   r«  r<   Ú)_cancel_secondary_profile_reconnect_tasksr¢  r|   )r{   r+  r-  Ústop_room_workerÚstop_watchdogs   &&   r!   Ú_stop_begin_teardownÚ)GatewayShutdownMixin._stop_begin_teardown  sB  é € ä�‰Ð+¸t×?V×?VÐ?V©^Ð\^Ô_ÜŸšÓ)ˆŒØˆŒØ×+Ñ+Ô-ØˆŒÝ=Ù# DÓ)×)Ð)Ø×.Ñ.Ô0ä" 4Ð)CÀTÓJÐÜÐ$×%Ò%ð	Ù-°cÔ:×:Ö:Ü—N‘NØrôô   Ð&>ÀÓEˆÜ�M×"Ò"Ù“/×!Ð!Ø×<Ñ<Ó>×>Ð>à×6Ñ6Ó8×8Ð8Ü�‰ÐKÈSÏ[É[Ë]Ö[ñ+ 	*ñ ;øô ô Ü—‘ØpØ!ð ö ðúñ "Ù>á8ùs~   ‚A4FÁ6EÁ72FÂ*E Â6EÂ7	E ÃE Ã(FÃ>FÃ?FÄFÄFÄ.F	Ä/+FÅE Å"FÅ?FÆFÆFÆFÆ	Fc               ó$   € V ^8„  d   QhRRRRRR/# ©r   r  ra   r+  rg   r   rÔ   r   )r    s   "r!   r"   rŠ   =  s(   € ÷ /`ñ /`°Uð /`ÐAbð /`Ðgkñ /`r#   c              ƒ  ón  "  € ^ RI Hp VP                  V R4      G Rj  x€L
 pV P                  4       pV P	                  4       pVP                  4       p\        V R\        4      p\        V \        V4      4      p	\        WW’P                  4       R7      p
V'       g	   V'       d   W¡8”  d   \        P                  RWVW¨V4       \        P                  ! 4       pV P!                  W4      G Rj  x€L
 w  Vn        Vn        \        P                  ! 4       V,
          Vn        \        P                  RVP                  4       VP&                  VP$                  \)        VP"                  4      V P+                  4       VV P                  4       W`P	                  4       WrP                  4       4       VP$                  '       d   R# V F9  pWÀP,                  9  g   K   V P.                  P1                  V4      G Rj  x€L
  K;  	  R#  ELà EL L  \2         d"   p\        P5                  RYÍ4        Rp?Kn  Rp?ii ; i5i)	zAPre-mark resume_pending, drain agents/cron/API work into ``ctx``.©ÚGatewayRunnerzpre-drain mark_resume_pendingNÚ_cron_drain_timeout)Úwatchdog_delayr|   u—   Shutdown drain: %d in-flight cron job(s), %d api_server run(s) â€” waiting up to %.0fs for them (cron_drain_timeout=%.0fs, restart_drain_timeout=%.0fs)zËShutdown phase: drain done at +%.2fs (drain took %.2fs, timed_out=%s, active_at_start=%d, active_now=%d, cron_at_start=%d, cron_now=%d, api_at_start=%d, api_now=%d, deferred_at_start=%d, deferred_now=%d)z2clear_resume_pending after drain failed for %s: %s)rè   r:  r;  rŽ   r�   ri   rH   r   r   r   r   r|   r'   r2   rx   ry   r   rn   ro   rq   r™   r�   r  r6  Úclear_resume_pendingr<   rº   )r{   r  r+  r:  Ú_pre_drain_keysÚ_cron_at_startÚ_api_at_startÚ_deferred_at_startÚ_cron_drain_cfgÚ_cron_leashÚ_cron_timeoutÚ_drain_started_atr9  rë  s   &&&           r!   Ú_stop_drain_active_workÚ,GatewayShutdownMixin._stop_drain_active_work=  sÓ  é € å-à -× SÑ SØÐ1ó!
÷ 
ˆð ×4Ñ4Ó6ˆØ×2Ñ2Ó4ˆØ ×/Ñ/Ó1Ðä! $Ð(=Ô?aÓbˆô 4°DÔ:YÐZaÓ:bÓcˆÜ1Ø°[Ï+É+Ë-ô
ˆ÷ Ÿm°Ô1HÜ�K‰KðYà¨}Èwôô
 !ŸNšNÓ,ÐØ15×1JÑ1JÈ7Ó1b×+bÑ(ˆÔ˜3œ=Ü ŸNšNÓ,Ð/@Õ@ˆÔÜ�‰ð5à69·k±k³mÀS×EVÑEVØ�M‰Mœ3˜s×0Ñ0Ó1°4×3LÑ3LÓ3NÐP^Ø×'Ñ'Ó)¨=×:TÑ:TÓ:VØ× 2Ñ 2Ó 4ô	
ð �=�=ˆ=Ùó #ˆCØ×.Ñ.Ö.ð`Ø×2Ñ2×GÑGÈÓL×LÒLó #òM
ò0 ,cñ" MøÜ ô `Ü—L‘LÐ!UÐWZ×_Ò_ûð`üsf   ‚H5žG>ŸBH5Â$AH5Ã+HÃ,C#H5ÇHÇ3HÇ4HÇ8H5ÈH5ÈHÈH2ÈH-È'H5È-H2È2H5c               ó    € V ^8„  d   QhRRRR/# r*  r   )r    s   "r!   r"   rŠ   n  s    € ÷ (\ñ (\Ð8Yð (\Ð^bñ (\r#   c           	   ƒ  ó–  a a"  € ^ RI Hp \        P                  RSP                  S P                  4       S P                  4       S P                  4       SP                  4       4       VP                  S R4      G Rj  x€L
  VP                  S 4      pS P                  V4       VP                  S 4      p\        P                  ! 4       pVP                  4       V,           p\        P!                  RV4       R VV 3R llpV! 4       '       dG   VP                  4       V8  d2   S P#                  R4       \        P$                  ! R	4      G Rj  x€L
  KS  V! 4       '       d'   S P                  V4       \        P'                  R
4       VP)                  R4      G Rj  x€L
 p\        P!                  RSP+                  4       4       \-        \.        P0                  R4      ;_uu_ 4        S P3                  V4      G Rj  x€L
  RRR4       \        P!                  RSP+                  4       4       R#  EL± Lã L— L9  + '       g   i     L?; i5i)z]Drain timed out: mark resume_pending, interrupt, settle, kill tool subprocesses, notify cron.r9  zªGateway drain timed out after %.1fs with %d active agent(s), %d in-flight cron job(s), %d api_server run(s), and %d deferred agent worker(s); interrupting remaining work.r7  Nz?Shutdown phase: allowing %.1fs for interrupted agents to unwindc               ó   € V ^8„  d   QhRR/# r   r   )r    s   "r!   r"   ÚIGatewayShutdownMixin._stop_interrupt_remaining_work.<locals>.__annotate__�  s   € ÷ 	Bñ 	B˜Dñ 	Br#   c                 ó´   <€ \        SP                  ;'       g<    \        S4      ;'       g)    SP                  4       ;'       g    S P	                  4       4      # rw   )r   r  r   r�   ri   )r+  r{   s   €€r!   Ú
_work_liveÚGGatewayShutdownMixin._stop_interrupt_remaining_work.<locals>._work_live�  sa   ø€ Ü˜×,Ñ,÷  Að  AÔ0BÀ4Ó0H÷  Að  AÈD×LfÑLfÓLh÷  Að  AÐlo×l~Ñl~ó  mAó  Bð  Br#   r¡  r  z?Re-signaled interrupt for work still live at settle-window exitzpost-interruptz7Shutdown phase: post-interrupt tool kill done at +%.2fsz&Cron interrupt notification failed: %sz5Shutdown phase: cron interrupt notices done at +%.2fs)rè   r:  r'   r«  rq   r�   rŽ   r�   ri   r;  r.  r(  Ú_post_interrupt_grace_timeoutr£  r  rx   r2   r�  r¤  rº   r'  r|   rB   r
  r  rf  )	r{   r+  r:  r¶   Úinterrupt_grace_timeoutr  Úinterrupt_deadlinerM  Ú_interrupted_cron_jobss	   ff       r!   Ú_stop_interrupt_remaining_workÚ3GatewayShutdownMixin._stop_interrupt_remaining_workn  sÁ  ùé € å-Ü�‰ð+à,/×,=Ñ,=¸t×?XÑ?XÓ?ZØ×'Ñ'Ó)¨4×+EÑ+EÓ+GÈ×I[ÑI[ÓI]ô		
ð ×AÑAÀ$ÐH]Ó^×^Ð^Ø×9Ñ9¸$Ó?ˆØ×&Ñ& vÔ.Ø"/×"MÑ"MÈdÓ"SÐÜ×'Ò'Ó)ˆØ!ŸY™Y›[Ð+BÕBÐÜ�‰ÐUÐWnÔo÷	Bñ 	Bñ �lŠl˜tŸy™y›{Ð-?Ô?Ø×'Ñ'¨
Ô3Ü—-’- Ó$×$Ò$ñ �<Š<Ø×*Ñ*¨6Ô2Ü�L‰LÐZÔ[ð (5×'ZÑ'ZÐ[kÓ'l×!lÐÜ�‰ÐMÈsÏ{É{Ë}Ô]äœWŸ]™]Ð,T×UÕUð ×4Ñ4Ð5KÓL×LÐL÷ Vô 	�‰ÐKÈSÏ[É[Ë]Ö[ò; 	_ñ %ñ "mñ M÷ V×Uüsn   „A8I	Á<H-Á=BI	Ä?I	ÅH0ÅI	Å :I	ÆH2ÆA
I	Ç%H6Ç:H4Ç;H6Ç?/I	È0I	È2I	È4H6È6I	ÉI	c               ó    € V ^8„  d   QhRRRR/# r*  r   )r    s   "r!   r"   rŠ   ˜  s    € ÷  Zñ  ZÐ<]ð  ZÐbfñ  Zr#   c              ƒ  ó  "  € V P                   '       dV   V P                  '       dD   \        \        P                  R4      ;_uu_ 4        V P                  4       G Rj  x€L
  RRR4       V P                  VP                  4      G Rj  x€L
  \        V RR4      p\        V RR4      pVe‡   Veƒ   V;_uu_ 4        \        VP                  4       4      pVP                  4        RRR4       X F@  w  rVT P                  \        V\        4      '       d
   V^ ,          MTRVR7      G Rj  x€L
  KB  	  \        V RR4      pVe   V! 4       G Rj  x€L
  \        V P                  P                  4       4       F  w  r‰V P!                  W˜4      G Rj  x€L
  K   	  \        V R/ 4      p
\        V
P                  4       4       FR  w  r¼\        VP                  4       4       F   w  r‰V P!                  W˜VR	7      G Rj  x€L
  K"  	  VP                  4        KT  	  V
P                  4        \"        P%                  R
VP'                  4       4       R#  ELë  + '       g   i     ELò; i EL×  + '       g   i     ELˆ; i ELN EL- Lî Lˆ5i)zRDetached restart launch, agent finalization, idle-cache cleanup, adapter teardown.z-Failed to launch detached gateway restart: %sNÚ_agent_cache_lockÚ_agent_cachezshutdown idle-cacherµ  Ú&_cancel_process_completion_batch_tasksrG  rˆ  z3Shutdown phase: all adapters disconnected at +%.2fs)r1   rÓ  rB   r
  rÌ  ru  r»  rn   rH   rÎ   rê   Úclearr¹  rÌ   rP   r¢   Ú_bounded_adapter_teardownr'   r2   r|   )r{   r+  Ú_cache_lockÚ_cacheÚ_idle_agentsÚ_keyÚ_entryÚcancel_completion_batchesr>  rÎ  rG  Ú_profÚ_amaps   &&           r!   Ú"_stop_finalize_agents_and_adaptersÚ7GatewayShutdownMixin._stop_finalize_agents_and_adapters˜  s  é € à×"×"Ð" t×'=×'=Ð'=Ü ¤§¡Ð0_×`Õ`Ø×;Ñ;Ó=×=Ð=÷ aà×,Ñ,¨S×->Ñ->Ó?×?Ð?ä˜dÐ$7¸Ó>ˆÜ˜˜~¨tÓ4ˆØÒ" vÒ'9ß’Ü# F§L¡L£NÓ3�Ø—‘”÷ ó !-‘�à×<Ñ<Ü!+¨F´E×!:Ò!:�F˜1–IÀÐPeØ $ð =ó ÷ ò ñ !-ô %,¨DÐ2ZÐ\`Ó$aÐ!Ø$Ò0Ù+Ó-×-Ð-Ü!% d§m¡m×&9Ñ&9Ó&;Ö!<ÑˆHØ×0Ñ0°ÓC×CÒCñ "=ô $ DÐ*=¸rÓBÐÜ Ð!2×!8Ñ!8Ó!:Ö;‰LˆEÜ%)¨%¯+©+«-Ö%8Ñ!�Ø×4Ñ4°WÐPUÐ4ÓV×VÒVñ &9à�K‰KŽMñ <ð 	×ÑÔ!Ü�‰ÐIÈ3Ï;É;Ë=ÖYò9 >÷ a×`Ð`úâ?÷
 —�úò
ò .áCñ
 Wùsž   ‚$J §"J Á	IÁIÁIÁ"&J ÂIÂ	0J Â9*I"Ã#AJ Ä(I6Ä)#J ÅI9ÅA J ÆI<ÆA'J Ç5I>Ç6AJ ÉIÉI	É
J É"I3	É-
J É9J É<J É>J c               ó    € V ^8„  d   QhRRRR/# r*  r   )r    s   "r!   r"   rŠ   º  s    € ÷ 2]ñ 2]Ð5Vð 2]Ð[_ñ 2]r#   c              ƒ  ó¬  "  € ^ RI Hp \        V P                  4       F3  pW0P                  J g   W0P
                  J d   K#  VP                  4        K5  	  V P                  P                  4        V P                  4       Vn	        V P                  P                  4        \        V P                  4       F  pV P                  V4       K  	  \        \        4      ;_uu_ 4        ^ RIHp V! \#        V P$                  4      RR7       RRR4       \        \        4      ;_uu_ 4        ^ RIHp T! \#        \)        V RR4      ;'       g    / 4      P+                  4        UUu/ uF  w  rxV'       g   K  V\        V4      bK  	  uppRR7       RRR4       V P                  P                  4        V P,                  P                  4        V P$                  P                  4        V P.                  P                  4        R F/  p	\1        W	4      '       g   K  \)        W	4      P                  4        K1  	  V P2                  P5                  4        VP7                  R4      G Rj  x€L
  \8        P;                  R	VP=                  4       4       R
 R lp
\>        PA                  RV
4       R#   + '       g   i     EL§; iu uppi   + '       g   i     EL=; i Lt5i)zZCancel background tasks, flush pending messages, clear per-session state, final tool kill.r9  )Úflush_pending_to_filer´  r  N)Úflush_overflow_to_fileÚ_queued_eventszfinal-cleanupz6Shutdown phase: final-cleanup tool kill done at +%.2fsc               ó   € V ^8„  d   QhRR/# rc  r   )r    s   "r!   r"   ÚFGatewayShutdownMixin._stop_release_runtime_state.<locals>.__annotate__ã  s   € ÷ 	&ñ 	& 4ñ 	&r#   c                 ó    € ^ RI Hp  V ! 4        R# )r•   ©Úshutdown_cached_clientsN)rí  ro  rn  s    r!   Ú_reap_aux_clientsÚKGatewayShutdownMixin._stop_release_runtime_state.<locals>._reap_aux_clientsã  s   € õ GÙ#Ö%r#   zshutdown_cached_clients error)Ú_active_session_leasesÚ_busy_ack_ts)!rè   r:  rÎ   r	  Ú
_stop_taskrÕ  ÚcancelrZ  r�   rr   r¢   r  Ú_release_running_agent_stater   r<   r¬  rh  rm   Ú_pending_messagesri  rH   rê   Ú_running_agents_tsÚ_pending_approvalsrê  Ú_shutdown_eventré   r'  r'   r2   r|   re   rì  )r{   r+  r:  Ú_taskÚ_session_keyrh  ri  Ú_kÚ_vÚ_attrrp  s   &&         r!   Ú_stop_release_runtime_stateÚ0GatewayShutdownMixin._stop_release_runtime_stateº  s  é € å-Ü˜$×0Ñ0Ö1ˆEàŸ™Ó'¨5×4FÑ4FÓ+FÙØ�L‰LŽNñ	 2ð 	×Ñ×$Ñ$Ô&Ø×1Ñ1Ó3ˆŒØ�‰×ÑÔÜ  ×!5Ñ!5Ö6ˆLØ×-Ñ-¨lÖ;ñ 7ô ”i× Õ ÝDÙ!¤$ t×'=Ñ'=Ó">ÀzÕR÷ !ô ”i× Õ ÝEÙ"Ü,0´¸Ð?OÐQUÓ1V×1\Ð1\ÐZ\Ó,]×,cÑ,cÔ,eÔlÑ,e¡& "Ôik”�”T˜"“X’Ñ,eÒlØ!õ÷ !ð 	×Ñ×"Ñ"Ô$Ø×Ñ×%Ñ%Ô'Ø×Ñ×$Ñ$Ô&Ø×Ñ×%Ñ%Ô'Û?ˆEÜ�t×#Ô#Ü˜Ó$×*Ñ*Ö,ñ @ð 	×Ñ× Ñ Ô"ð ×AÑAÀ/ÓR×RÐRÜ�‰ÐLÈcÏkÉkËmÔ\õ	&ô 	×(Ñ(Ð)HÐJ[Ö\÷C !× Ð üó m÷ !× Ð úñ" 	Sùsc   ‚C KÃ"$J$ÄKÄ%:J>ÅJ8Å0J8Æ 
J>Æ
BKÈAKÉKÉAKÊ$J5	Ê/	KÊ8J>Ê>K	Ë	
Kc               ó$   € V ^8„  d   QhRRRRRR/# r7  r   )r    s   "r!   r"   rŠ   î  s-   € ÷ MUñ MU¸5ð MUÐGhð MUÐmqñ MUr#   c           	     óò  a a€ ^ RI HoHp \        R\	        V\        V4      VP                  4       ,
          R,
          4      4      pSP                  S VR7      pV'       d   \        P                  RWT4       R# \        P                  RVP                  4       4       S P                  4       pVP                  pS P                  4       pVP                  4       \        S 4      ,           p	V'       g   V'       g   V'       g	   V	'       d   \        P                  RWgW‰4       R# \         P"                  p
\%        S R	R4      p\%        VR
V4      p\%        S RR4      pV\%        VR
R4      3 F0  pVf   K	  \'        VR4      '       g   K  V
! RVP(                  4       K2  	  \%        VRR4      pVe
   V
! RV4       V
! RVV 3R l4       R R lpV
! RV4       \        P                  RVP                  4       4       R# )zSQuiesce the executor, then close SessionDB handles only if no worker is still live.)r:  Ú_EXECUTOR_QUIESCE_TIMEOUTrp   r	  )Údrain_timeoutuÖ   Shutdown phase: %d executor worker(s) still running after a %.2fs quiesce â€” skipping the SessionDB close/checkpoint to avoid racing a live write (#101093); handles are left open for SQLite to recover on next openNz+Shutdown phase: executor quiesced at +%.2fsu  Shutdown phase: %d cron job(s) / %d API-server run(s) / %d API-server worker(s) / %d deferred/authority worker(s) still running after the executor quiesce â€” skipping the SessionDB close/checkpoint, leaving state.db open for the live writer (#102198, #116535)Ú_session_dbÚ_dbrk  rè  zSessionDB close errorÚclose_all_db_handleszSessionDB handle sweep errorz#Runner SessionDB handle sweep errorc                 ó&   <€ S P                  S4      # rw   )Úclose_all_session_db_handles)r:  r{   s   €€r!   rw  ÚJGatewayShutdownMixin._stop_quiesce_and_close_session_dbs.<locals>.<lambda>/  s   ø€ ¸]×=gÑ=gÐhlÔ=mr#   c               ó   € V ^8„  d   QhRR/# rc  r   )r    s   "r!   r"   ÚNGatewayShutdownMixin._stop_quiesce_and_close_session_dbs.<locals>.__annotate__1  s   € ÷ 	[ñ 	[˜tñ 	[r#   c                 ó`   € ^ RI Hp  V ! 4       pV'       d   \        P                  RV4       R# R# )r•   )Ú	close_allz2Closed %d shared SessionDB instance(s) at shutdownN)Úhermes_state_registryr�  r'   rº   )r�  Úcloseds     r!   Ú_close_sharedÚOGatewayShutdownMixin._stop_quiesce_and_close_session_dbs.<locals>._close_shared1  s&   € õ 8Ù“[ˆFßÜ—‘ÐQÐSYÖZñ r#   zShared SessionDB close errorz.Shutdown phase: SessionDB close done at +%.2fs)rè   r:  r„  r¦   r¥  r   r|   Ú_shutdown_executorr'   r«  r2   rŽ   rr   r²   ri   r   re   rì  rH   rê  rè  )r{   r  r+  r„  Ú_exec_quiesce_budgetÚ
_exec_liveÚ
_cron_liveÚ	_api_liveÚ_api_worker_liveÚ_deferred_liverõ  Ú_self_dbÚstorer‡  Ú_sweepr’  r:  s   f&&             @r!   Ú#_stop_quiesce_and_close_session_dbsÚ8GatewayShutdownMixin._stop_quiesce_and_close_session_dbsî  s¶  ù€ çHô   #Ø”Ð.Ô0OÐPWÓ0XÐ[^×[fÑ[fÓ[hÕ0hÐknÕ0nÓoó 
Ðð #×5Ñ5°dÐJ^Ð5Ó_ˆ
ßô �N‰Nð:à;Eôñ
 Ü�‰ÐAÀ3Ç;Á;Ã=ÔQð ×0Ñ0Ó2ˆ
Ø—L‘Lˆ	Ø×8Ñ8Ó:ÐØ×+Ñ+Ó-Ô0BÀ4Ó0HÕHˆßŸ×&6¿.Ü�N‰Nðað Ð'7ô	ñ Ü$×0Ñ0ˆô ˜4 °Ó5ˆÜ˜8 U¨HÓ5ˆÜ˜˜o¨tÓ4ˆØœg e¨U°DÓ9Ó:ˆCØŒ¤7¨3°×#8Ô#8ÙÐ-¨s¯y©yÖ9ñ ;ô ˜Ð 6¸Ó=ˆØÒÙÐ0°&Ô9áÐ3Õ5mÔnõ	[ñ 	Ð,¨mÔ<Ü�‰ÐDÀcÇkÁkÃmÖTr#   c               ó    € V ^8„  d   QhRRRR/# r*  r   )r    s   "r!   r"   rŠ   =  s    € ÷ ;Mñ ;MÐ2Sð ;MÐX\ñ ;Mr#   c              ƒ  óª  "  € ^ RI HpHpHp ^ RIHp \        V RR4      '       d	   V! V 4      # VP                  '       g9   \        \        4      ;_uu_ 4        VR,          P                  4        RRR4       M\        P                  R4       VP                  '       d3   V P                  \        VP                  P!                  4       4      4       V P"                  '       d‰   V P$                  f{   \'        \(        P*                  R4      ;_uu_ 4        V! V! 4       R	\,        P,                  ! 4       R
\/        V P0                  4      R\/        V P2                  4      /RR7       RRR4       V P"                  '       d9   V P0                  '       d'   \4        V n        V P8                  ;'       g    RV n        RV n        \        V RR4      '       dE   V P"                  '       g3   \        P                  R4       V P=                  RV P8                  4       MV P=                  RV P8                  4        ^ RIH p Rp\        V RR4      p\        V RR4      p	V	'       dN   \C        V\D        \F        34      '       d2   V^ 8”  d+   \I        R\K        WxVPM                  4       ,
          4      4      pV! VR7      G Rj  x€L
 '       g   \        PO                  R4       T! T 4       \        P                  RTPM                  4       4       R#   + '       g   i     EL…; i  + '       g   i     EL»; i Lu  \         d    \        PQ                  RRR7        L|i ; i5i)z†Clean-shutdown marker, restart markers, terminal runtime status; process bootstrap
releases PID/lock ownership after the writer drain.)ró  Ú"_planned_restart_notification_pathÚ_shutdown_gateway_health_export)Úatomic_json_writeÚ_startup_cleanup_onlyFz.clean_shutdownNu   Skipping .clean_shutdown marker â€” drain timed out with interrupted agents; next startup will recover their interrupted turns.z7Failed to write planned restart notification marker: %sÚrequested_atrÃ  rÂ  )ÚindentzGateway restart requestedÚ_signal_initiated_shutdownuŠ   Gateway stopped by an unexpected signal â€” persisting gateway_state=running so container_boot auto-starts on the next boot (issue #42675)ÚrunningÚstopped)Úflush_runtime_status_asyncg       @Ú_launchd_exit_timeout_sÚ_stop_requested_by_signalrp   r×  z2Timed out flushing terminal gateway runtime statusz/Failed to flush terminal gateway runtime statusTr  z&Gateway stopped (total teardown %.2fs)))rè   ró  r¢  r£  Úutilsr¤  rH   ro   r   r<   Útouchr'   r2   rn   r  ré   Úkeysr1   r—  rB   r
  r  rx   r   r3   rÓ  r   Ú
_exit_codeÚ_exit_reasonrÇ  r�  Úgateway.statusr«  rÌ   r8   ra   r¦   r¥  r|   r«  rº   )
r{   r+  ró  r¢  r£  r¤  r«  Úflush_timeoutÚbudgetÚsignal_stops
   &&        r!   Ú_stop_persist_exit_stateÚ-GatewayShutdownMixin._stop_persist_exit_state=  sŽ  é € ÷ 	rÑqÝ+Ü�4Ð0°%×8Ò8Ù2°4Ó8Ð8ð �}�}ˆ}Üœ)×$Õ$ØÐ 1Õ1×8Ñ8Ô:÷ %Ð$ô �K‰Kð Mô Nð ××ÐØ×2Ñ2´3°s×7HÑ7H×7MÑ7MÓ7OÓ3PÔQØ×"×"Ð" t×'CÑ'CÒ'KÜ ¤§¡Ð0i×jÕjÙ!Ù6Ó8à&¬¯	ª	«Ø%¤t¨D×,EÑ,EÓ'FØ"¤D¨×)?Ñ)?Ó$@ðð
  õ÷ kð ×"×"Ð" t×'@×'@Ð'@ä?ˆDŒOØ $× 1Ñ 1× PÐ PÐ5PˆDÔØˆŒô �4Ð5°u×=Ò=Àd×F]×F]ÐF]Ü�K‰Kðfôð ×'Ñ'¨	°4×3DÑ3DÕEà×'Ñ'¨	°4×3DÑ3DÔEð	[ÝAð  ˆMÜ˜TÐ#<¸dÓCˆFÜ! $Ð(CÀUÓKˆKßœz¨&´3¼°,×?Ò?ÀFÈQÄJÜ # C¬¨]ÀSÇ[Á[Ã]Õ<RÓ)SÓ T�Ù3¸MÔJ×JÖJÜ—‘ÐSÔTñ 	(¨Ô-Ü�‰Ð<¸c¿k¹k»mÖL÷c %×$Ð$ú÷ k×jÐjúñF Køäô 	[Ü�L‰LÐJÐUYˆLÖZð	[üsŸ   ‚=MÁ MÁLÁ//MÂAMÃ#0MÄALÅ#MÅ=MÆMÆ-3MÇ!AMÈ0BL+ Ê3L)Ê4	L+ Ê>L+ Ë.MÌL	Ì	MÌL&	Ì 	MÌ)L+ Ì+"MÍMÍMÍMc               ó    € V ^8„  d   QhRRRR/# )r   r+  rg   r   rm   r   )r    s   "r!   r"   rŠ   z  s   € ÷ 
ñ 
Ð/Pð 
ÐUYñ 
r#   c                ó¤  € R\        V P                  4      R\        V P                  4      R\        V P                  4      RV P	                  4       RV P                  4       RV P                  4       RVP                  4       RV P                  R	\        V 4      R
\        V RR4      R\        V 4      RVP                  e   VP                  4       /# R/# )zGState dumped by the thread-based shutdown watchdog when teardown hangs.Úrestart_requestedr¡  r©  rn   Úactive_cron_jobsÚactive_api_runsÚactive_deferred_agent_workersÚrestart_drain_timeoutÚeffective_drain_timeoutÚlaunchd_exit_timeout_sr¬  NÚwatchdog_delay_sÚphase_elapsed_s)r   r1   rÇ  r¦  r�   rŽ   r�   ri   r`  r   rH   rc   rk   r|   )r{   r+  s   &&r!   Ú_shutdown_watchdog_snapshotÚ0GatewayShutdownMixin._shutdown_watchdog_snapshotz  sÇ   € ð  ¤ d×&=Ñ&=Ó!>Øœ˜TŸ^™^Ó,Ø”t˜DŸM™MÓ*Ø˜T×6Ñ6Ó8Ø × ;Ñ ;Ó =Ø˜t×9Ñ9Ó;Ø+¨S×-?Ñ-?Ó-AØ# T×%@Ñ%@Ø%Ô'CÀDÓ'IØ$¤g¨dÐ4MÈtÓ&TØÔ 9¸$Ó ?Ø°·±Ò0J˜sŸ{™{›}ð
ð 	
ð QUð
ð 	
r#   c               ó   € V ^8„  d   QhRR/# rc  r   )r    s   "r!   r"   rŠ   ‹  s   € ÷ &!ñ &! $ñ &!r#   c              ƒ  ó*  a aa"  € ^ RI Ho \        P                  ! 4       pVS n        \
        P                  \        S RR 4      R7      o\        P                  P                  R4      '       g   \        \        S 4      VVVV 3R l^R7        SP                  S S4      G Rj  x€L
  \        S 4      pVS P                  8  d-   \         P#                  R	VS P                  S P$                  4       SP'                  S VS4      G Rj  x€L
  SP(                  '       d   SP+                  S S4      G Rj  x€L
  ^ R
IHp V! S \        S 4      SP1                  4       ,
          SP3                  S 4      4      G Rj  x€L
  SP5                  S S4      G Rj  x€L
  SP7                  S S4      G Rj  x€L
  SP9                  S VS4       SP;                  S S4      G Rj  x€L
  VP=                  4        R#  ELL Lë LÁ L~ Lf LN L#  TP=                  4        i ; i5i)zERun every ``_stop_*`` phase under the thread-based shutdown watchdog.r9  r�   c                 ó   € ^ # r,  r   r   r#   r!   rw  Ú1GatewayShutdownMixin._stop_impl.<locals>.<lambda>—  s   € ÑXYr#   )ri   ÚPYTEST_CURRENT_TESTc                 ó(   <€ S P                  SS4      # rw   )rÄ  )r:  r+  r{   s   €€€r!   rw  rÉ  œ  s   ø€  M×$MÑ$MÈdÐTWÔ$Xr#   )Ú
done_eventÚsnapshot_fnr/   Nu|   Shutdown drain capped to %.0fs (configured %.0fs) to fit the live launchd exit timeout of %.0fs â€” launchd SIGKILLs past it)Úsettle_gateway_runtime)rè   r:  Ú	threadingÚEventÚ_shutdown_watchdog_donere   r‡   rH   rL  rM  r£   r   rc   r4  r   r`  r'   r«  r¬  rF  ro   rS  r0  rÎ  r|   r.  rd  r€  rž  r·  ré   )r{   Ú_watchdog_doner  rÎ  r:  r+  s   f   @@r!   Ú
_stop_implÚGatewayShutdownMixin._stop_impl‹  sÜ  úé € å-ô #ŸšÓ*ˆØ'5ˆÔ$ä"×/Ñ/Ü" 4Ð)NÑPYÓZð 0ó 
ˆô �z‰z�~‰~Ð3×4Ò4Ü!Ü)¨$Ó/¸NÞXÐdeõð	!Ø×4Ñ4°T¸3Ó?×?Ð?Ü2°4Ó8ˆGØ˜×4Ñ4Ô4Ü—‘ðDà˜T×8Ñ8¸$×:VÑ:Vôð
  ×7Ñ7¸¸gÀsÓK×KÐKØ�}�}ˆ}Ø#×BÑBÀ4ÈÓM×MÐMÝBÙ(¨Ô/HÈÓ/NÐQT×Q\ÑQ\ÓQ^Õ/^Ð`m÷  aIñ  aIð  JNó  aOó  P÷  Pð  PØ×BÑBÀ4ÈÓM×MÐMØ×;Ñ;¸DÀ#ÓF×FÐFØ×=Ñ=¸dÀGÈSÔQØ×8Ñ8¸¸sÓC×CÐCà×ÑÖ ò% @ñ LáMñ PÙMÙFáCøà×ÑÕ üs¤   …BHÂG> Â#G/Â$A"G> ÄG2ÄG> ÄG> Ä2G4Ä3AG> Å7G6Å8G> ÆG8ÆG> Æ+G:Æ,,G> ÇG<ÇG> ÇHÇ/G> Ç2G> Ç4G> Ç6G> Ç8G> Ç:G> Ç<G> Ç>HÈHrB  rÊ  rË  c               ó(   € V ^8„  d   QhRRRRRRRR/# )r   rB  r   rÊ  rË  r   rÔ   r   )r    s   "r!   r"   rŠ   ³  s,   € ÷ ñ ØðØ:>ðØY]ðà	ñr#   c             ƒ  óf  "  € ^ RI Hp \        V RR4      p\        V4      '       d   V! 4        V'       d   RV n        W n        W0n        V P                  e   V P                  G Rj  x€L
  R# \        P                  ! VP                  V 4      4      V n        V P                  G Rj  x€L
  R#  LF L5i)z-Stop the gateway and disconnect all adapters.r9  Ú_stop_loop_liveness_guardsNT)rè   r:  rH   r¥   r1   rÓ  r3   rt  r£  rÑ  rÓ  )r{   rB  rÊ  rË  r:  Ú_stop_guardss   &$$$  r!   rÍ  ÚGatewayShutdownMixin.stop³  s�   é € õ 	.ô ˜tÐ%AÀ4ÓHˆÜ�L×!Ò!ÙŒNßØ&*ˆDÔ#Ø%5Ô"Ø(7Ô%Ø�?‰?Ò&Ø—/‘/×!Ð!ÙÜ!×-Ò-¨m×.FÑ.FÀtÓ.LÓMˆŒØ�o‰o×Òñ "ñ 	ùs(   ‚3B1¶0B1Á&B-Á'?B1Â&B/Â'B1Â/B1c               ó   € V ^8„  d   QhRR/# rc  r   )r    s   "r!   r"   rŠ   Ç  s   € ÷ *ñ *¨ñ *r#   c              ƒ  óV   "  € V P                   P                  4       G Rj  x€L
  R#  L5i)zWait for shutdown signal.N)rz  Úwaitrz   s   &r!   Úwait_for_shutdownÚ&GatewayShutdownMixin.wait_for_shutdownÇ  s   é € à×"Ñ"×'Ñ'Ó)×)Ô)ùs   ‚) '¡))rÏ  rÄ   rm  rÇ  r±  r²  rÓ  r~  rÓ  r1   rÕ  rÒ  r3   r¦  r†  r§  r©  rÑ  rt  rÝ  )rš  )r	  rw   )`r~   r   r€   r�   r‚   r„   Ú	dataclassr‡   r‘   Ústaticmethodrš   rŽ   r¨   r�   r²   r»   rÀ   r�   rà   rï   r  r  r!  r8  rQ  rW  r`  rh  r‚  rŠ  rˆ  r”  r±  r®  rº  r¬  rª  rÔ  rØ  rß  r   rê  rö  rû  rÿ  r   r(  r.  r;  rA  rF  rf  rs  r}  rT  r¢  r°  r»  rÃ  rÓ  r¹  rÝ  rõ  rþ  r  r  r  r7  r[  ru  r{  rƒ  rz  r‹  ry  r¡  r»  rÀ  rÖ  râ  ræ  rì  r#  r'  r4  rF  rS  rd  r€  rž  r·  rÄ  rÓ  rÍ  rÝ  r†   r   r#   r!   re   re   ­   s  † Ùaà×Ñ÷6ð 6ó ð6õ 
ð ô	*ó ð	*õõ EõõõõõGõ2õ$õ(ð2 ô	>ó ð	>õ\õ

õ*'ðR ñó ðõ
õZõ 
õ8	5õ2ò1÷`UõD+2õZ9õ&
õ"õ&
0õ;÷"*ð(
Ø@Dð
à$(ð
àIMð
ð +/ð
ð GM÷
ð"
À÷ 
õ$õ
÷&#õPOõ"tõõ W÷

õ@õDoõ,	ð öó ðõ$kJð\ ô%có ð%cõNvõ"õ*Nð,cØ,.ðcØMQ÷cõ@ *õF4ð ôó ðõ^õ
õ6Pð ô1ó ð1ðf ô-ó ð-õ^
õ:Oõ
`õ:õ@õ
õ4
õ*&õP@ðD °%ð  ÈU÷  õDõð ôó ðð ô<!ó ð<!ð| ô
ó ð
õ"\õ>/`õb(\õT ZõD2]õhMUõ^;Mõz
õ"&!ðPØ!&ðØAFðØ`e÷÷(*ñ *r#   re   );r‚   Ú
__future__r   r£  r„   rû  r
  rL  re  ro  rÏ  rx   Ú
contextlibr   r   r   Úcontextvarsr   Úpathlibr   Útypingr   r	   r
   r   Ú
agent.i18nr   r/  r   Úgateway.restartr   r   r   r   r   Úgateway.run_commonr   r0  r   r   r   Ú gateway.run_shutdown_session_endr   Úgateway.shutdown_watchdogr   r   Ú	getLoggerr'   r+   r4   ÚstriprQ  rB   rJ   rM   rT   r^   rc   re   r   r#   r!   Ú<module>rí     sà   ðñõ #ã Û Û Û Û 	Û Û 
Û Û ß <Ñ <Ý Ý ß 0Ó 0å Ý #÷õ õ &ß ]Ñ ]Ý Cß \ð 
×	Ò	˜=Ó	)€õõð0-÷Z 
�EƒGð[ ð` ð/À%ö /ó ð/õ Lõ
Cõ
Qð
BÐ^b÷ Bõxô\ *Ð1ö \ *r#   