
    sj                     p   d Z ddlZddlmZ ddlmZ dZdZdZ e	ed         d	                   Z
 e	ed         d
                   Z e	ed         d                   ZdZ	 d!deeef         dz  defdZdefdZdede	fdZdede	fdZdede	fdZdedde	de	de	de	de	de	fdZddde	de	de	de	fd ZdS )"zBShared gateway restart constants and supervisor detection helpers.    N)Mapping)DEFAULT_CONFIGK   N   "HERMES_GATEWAY_EXTERNAL_SUPERVISORagentrestart_drain_timeoutrestart_after_turn_timeoutcron_drain_timeoutg      $@environreturnc                 Z   | t           j        n| }|                    d          rdS |                    d          rdS |                    dd          }|r|dk    rdS t          |                    t          d                                                                                    dv S )	z=Return whether this gateway process is owned by a supervisor.NINVOCATION_IDTHERMES_S6_SUPERVISED_CHILDXPC_SERVICE_NAME 0>   1onyestrue)osr   getstrEXTERNAL_GATEWAY_SUPERVISOR_ENVstriplower)r   envxpc_services      3/home/agent/.hermes/hermes-agent/gateway/restart.pyis_gateway_supervisor_processr!   7   s      "**WC
ww t
ww+,, t'',b11K {c))tsww6;;<<BBDDJJLL Q      c                  ~    t           j                            d          pt           j                            d          S )u  Return whether the gateway is running inside a container for restart
    routing purposes (Docker/Podman ⇒ the detached setsid path dies with the
    cgroup; exit-75 service restart is the only viable path).

    Extracted from the inline probe in the /restart handler so tests can mock
    container detection hermetically — a real ``/.dockerenv`` on a
    containerized CI runner otherwise flips the routing under the test.
    z/.dockerenvz/run/.containerenv)r   pathexists r"   r    is_container_restart_contextr'   K   s,     7>>-((PBGNN;O,P,PPr"   rawc                     	 t          | pd                                          rt          |           nt          }n# t          t
          f$ r
 t          cY S w xY wt          d|          S )zEParse a configured drain timeout, falling back to the shared default.r           )r   r   float%DEFAULT_GATEWAY_RESTART_DRAIN_TIMEOUT	TypeError
ValueErrormaxr(   values     r    parse_restart_drain_timeoutr2   W   sn    5!#)nn2244_c


:_z" 5 5 544445sE??s   9< AAc                     | t           S t          | t                    r|                                 st           S 	 t	          |           }n# t
          t          f$ r
 t           cY S w xY wt          d|          S )u   Parse the after-turn wait cap for in-band restart, falling back to default.

    ``0`` is a deliberate disable (legacy immediate drain) and must not fall
    through to the default — unlike empty/missing input.
    Nr*   )*DEFAULT_GATEWAY_RESTART_AFTER_TURN_TIMEOUT
isinstancer   r   r+   r-   r.   r/   r0   s     r     parse_restart_after_turn_timeoutr6   `   s     {99#s :CIIKK :99:c

z" : : :9999:sE??   A A&%A&c                     | t           S t          | t                    r|                                 st           S 	 t	          |           }n# t
          t          f$ r
 t           cY S w xY wt          d|          S )u  Parse the cron-only drain floor, falling back to the shared default.

    ``0`` is a deliberate opt-out — cron work is then interrupted on the same
    budget as chat work, the pre-#82161 behaviour — and must not fall through
    to the default, unlike empty/missing input.
    Nr*   )"DEFAULT_GATEWAY_CRON_DRAIN_TIMEOUTr5   r   r   r+   r-   r.   r/   r0   s     r    parse_cron_drain_timeoutr:   q   s     {11#s 2CIIKK 2112c

z" 2 2 211112sE??r7   r*   )elapsedcleanup_reserve_sdrain_timeoutwatchdog_delayr;   r<   c                    ddt           dt          dt          fd} ||           } ||          }|dk    r|S  ||           ||          z
   ||t                    z
  }t          |t	          ||                    S )u  Seconds the shutdown drain may spend waiting on in-flight cron work.

    The configured floor is clamped to what this process can actually honour.
    The shutdown watchdog hard-exits at ``watchdog_delay`` and the service
    manager's ``TimeoutStopSec`` is sized from the same drain timeout, so
    waiting past that leash (minus ``cleanup_reserve_s`` for the teardown that
    follows the drain) would swap a cleanly-interrupted job for a SIGKILL that
    leaves it wedged mid-run — strictly worse than the bug being fixed.

    Never returns less than ``drain_timeout``: the cron floor only ever
    extends the wait, so an operator who deliberately configured a long
    ``restart_drain_timeout`` keeps it.
    r*   r1   fallbackr   c                 p    	 t          t          |           d          S # t          t          f$ r |cY S w xY w)Nr*   r/   r+   r-   r.   )r1   r@   s     r    _secondsz+resolve_cron_drain_budget.<locals>._seconds   sF    	uU||S))):& 	 	 	OOO	s    55)r*   )objectr+   CRON_DRAIN_CLEANUP_RESERVE_Sr/   min)	r=   r   r>   r;   r<   rC   drainfloorceilings	            r    resolve_cron_drain_budgetrJ      s    ,  % %     H]##EH'((E||  
(7

	
($&B
C
C	D 
 uc%))***r"   g      .@)headroomafter_turn_timeoutrK   c                b   	 t          t          |           d          }n# t          t          f$ r d}Y nw xY w	 t          t          |          d          }n# t          t          f$ r d}Y nw xY w	 t          t          |          d          }n# t          t          f$ r d}Y nw xY w||z   |z   S )aT  Seconds a CLI should wait for the gateway PID to exit after SIGUSR1.

    In-band restart may defer ``stop()`` until active turns finish
    (``after_turn_timeout``) and then spend up to ``drain_timeout`` inside
    ``stop()``. Callers that fall back to a hard kill on wait expiry must
    cover both phases or they reintroduce #77184.
    r*   rB   )r=   rL   rK   rG   
after_turnmargins         r     resolve_restart_exit_wait_budgetrP      s    E-((#..z"   122C88

z"   


U8__c**z"   :&&s/     66A A.-A.2B B&%B&)N)__doc__r   collections.abcr   hermes_cli.configr   !GATEWAY_SERVICE_RESTART_EXIT_CODEGATEWAY_FATAL_CONFIG_EXIT_CODEr   r+   r,   r4   r9   rE   r   boolr!   r'   rD   r2   r6   r:   rJ   rP   r&   r"   r    <module>rW      s/   H H 				 # # # # # # , , , , , , %' ! "$ 
 #G (-734) ) % .3U789. . * &+U701& & "  $  )- S#X%	   (	Qd 	Q 	Q 	Q 	QV     & U    "& U    . ;%+ %+ %+%+%+ 	%+
 %+ %+ %+ %+ %+ %+X 	' ' ''' 	'
 ' ' ' ' ' 'r"   