+
    lHàj+!  ã                   óò   a € R t0 t R t^ RIt^ RIt^ RIt ! R R4      t]P                  ! 4       t/ t] ^ k Rt	Rt
R R ltR R	 ltR
 R ltR R ltR R lt]P                   RR R ll4       tRR R lltR# )u&  Human-wait accounting for :mod:`tools.approval` (per session).

Tracks wall-clock time the agent spends verifiably blocked on a HUMAN prompt
(CLI approval prompt, gateway approval round-trip). The concurrent tool batch
deadline in agent/tool_executor.py excludes this time so a slow human answer
never times a batch out â€” but ONLY this time. Measuring at the source (rather
than residency in the authorization gate, which is arbitrary code) is what keeps
a wedged pre_tool_call plugin or a dead approval client from growing the
exclusion 1:1 with wall clock and defeating the deadline entirely. Keyed by
session so one gateway session's pending approval cannot extend a different
session's batch deadline; state is process-global like the rest of the approval
state, bounded by _HUMAN_WAIT_MAX_SESSIONS.
Nc                   ó6   a € ] tR t^t o RtV 3R lR ltRtV tR# )Ú_HumanWaitStatec                ó   <€ V ^8„  d   QhRR/# )é   ÚreturnN© )ÚformatÚ__classdict__s   "€Ú>/home/ubuntu/.hermes/hermes-agent/tools/approval_human_wait.pyÚ__annotate__Ú_HumanWaitState.__annotate__!   s   ø€ ÷ %ñ %˜$ñ %ó    c                ó0   € ^ V n         RV n        RV n        R# )é    Nç        )ÚpendingÚwindow_startedÚcompleted_seconds)Úselfs   &r
   Ú__init__Ú_HumanWaitState.__init__!   s   € ØˆŒØ,0ˆÔØ!$ˆÖr   )r   r   r   N)Ú__name__Ú
__module__Ú__qualname__Ú__firstlineno__Ú	__slots__r   Ú__static_attributes__Ú__classdictcell__)r	   s   @r
   r   r      s   ø‡ € ØB€I÷%ö %r   r   é   g      N@c                ó$   € V ^8„  d   QhR\         /# )r   r   ©Úfloat)r   s   "r
   r   r   0   s   € ÷ jñ jœEñ jr   c                 óv   € ^ RI Hp  ^ RIHp \	        V\        V P                  4       4      \        ,           4      # )u¡  Max seconds a single window may contribute: approvals.timeout + margin.
Every legitimate human wait self-terminates at ``approvals.timeout`` (the CLI
prompt join and the gateway poll loop both enforce it), so a window that
overstays this ceiling is itself wedged and must not keep extending a batch
deadline. Also the bound on the authorization gate's serialization-lock
acquire in agent/tool_executor.py, so the two cannot drift. Never call while
holding ``_human_wait_lock`` â€” it reads the config cache.
``_get_approval_timeout`` caps at ``agent.deadline.MAX_SAFE_TIMEOUT_S`` so the
value is always safe for ``Lock.acquire(timeout=...)`` / ``Thread.join(timeout=...)``.©Úapproval_context)ÚMAX_SAFE_TIMEOUT_S)Útoolsr$   Úagent.deadliner%   Úminr!   Ú_get_approval_timeoutÚHUMAN_WAIT_MARGIN_S)r$   r%   s     r
   Úhuman_wait_ceilingr+   0   s-   € õ 'Ý1ÜÐ!¤5Ð)9×)OÑ)OÓ)QÓ#RÔUhÕ#hÓiÐir   c                óH   € V ^8„  d   QhR\         R\         R\         R\         /# )r   ÚstartedÚnowÚceilingr   r    )r   s   "r
   r   r   ?   s(   € ÷ 1ñ 1¤Uð 1´ð 1Äð 1Ì5ñ 1r   c                ó:   € \        \        RW,
          4      V4      # )z®Seconds an open window contributes: elapsed, floored at 0, capped. Shared
by the close-time accrual and the open-window read so the two clamps stay
identical by construction.r   )r(   Úmax)r-   r.   r/   s   &&&r
   Ú_clamped_window_secondsr2   ?   s   € ô Œs�3˜�Ó&¨Ó0Ð0r   c                ó0   € V ^8„  d   QhR\         R\        /# )r   Úsession_keyr   )Ústrr   )r   s   "r
   r   r   F   s   € ÷ ñ ¤3ð ¬?ñ r   c                ó  € \         P                  V 4      pVfi   \        \         4       FB  p\        \         4      \        8  d    M)\         V,          P
                  ^ 8X  g   K;  \         V KD  	  \        4       ;p\         V &   V# )a   Return (creating if needed) the wait state for *session_key*. Caller must
hold ``_human_wait_lock``. Evicts idle entries (no pending waiter)
insertion-order-first until the table is under the cap so an army of
short-lived session keys cannot grow it without bound. Entries with an open
window are never evicted (that would corrupt live accounting), so the cap is
best-effort under 256+ concurrently-pending sessions.)Ú_human_wait_statesÚgetÚlistÚlenÚ_HUMAN_WAIT_MAX_SESSIONSr   r   )r4   ÚstateÚkeys   &  r
   Ú_human_wait_stater>   F   sq   € ô ×"Ñ" ;Ó/€EØ‚}ÜÔ*Ö+ˆCÜÔ%Ó&Ô)AÔAÙÜ! #Õ&×.Ñ.°!Ö3Ü& sÒ+ñ	 ,ô
 3BÓ2CÐCˆÔ" ;Ñ/Ø€Lr   c                ó>   € V ^8„  d   QhR\         R,          R\         /# ©r   r4   Nr   ©r5   )r   s   "r
   r   r   X   s   € ÷ 6ñ 6œc D�jð 6¬Sñ 6r   c                 ó:   € V e   V # ^ RI Hp VP                  4       # )Nr#   )r&   r$   Úget_current_session_key)r4   r$   s   & r
   Ú_resolve_keyrD   X   s    € ØÒØÐÝ&Ø×3Ñ3Ó5Ð5r   c                ó$   € V ^8„  d   QhR\         /# )r   ÚlabelrA   )r   s   "r
   r   r   _   s   € ÷ 
7ñ 
7œcñ 
7r   c                ó„   a aa€  ^ RI Ho \        P                  ! 4       pRTRT/oT TT3R l#   \         d    R u # i ; i)záCallable that pings the agent's inactivity tracker (at most every ~10s)
while a human wait is parked, so the gateway watchdog does not kill the agent
while the user is still answering. No-op in minimal tool-only environments.)Útouch_activity_if_duec                  ó   € R # ©Nr   r   r   r
   Ú<lambda>Ú$activity_heartbeat.<locals>.<lambda>f   s   € ‘tr   Ú
last_touchÚstartc                  ó   <€ S! SS 4      # rJ   r   )rF   r<   rH   s   €€€r
   rK   rL   i   s   ø€ Ñ(¨°Ô6r   )Útools.environments.baserH   Ú	ExceptionÚtimeÚ	monotonic)rF   r.   r<   rH   s   f @@r
   Úactivity_heartbeatrT   _   sD   ú€ ðÝAô �.Š.Ó
€CØ˜3 ¨Ð-€EÞ6Ð6øô	 ô ÙÒðús   …. ®?¾?c                ó2   € V ^8„  d   QhR\         R,          /# )r   r4   NrA   )r   s   "r
   r   r   m   s   € ÷  0ñ  0¤3¨¥:ñ  0r   c              #  óf  "  € \        V 4      p\        P                  ! 4       p\        ;_uu_ 4        \	        V4      pVP
                  ^ 8X  d   W#n        V;P
                  ^,          un        RRR4        Rx € \        P                  ! 4       p\        4       p\        ;_uu_ 4        \        P                  V4      pVem   V;P
                  ^,          un        VP
                  ^ 8X  dC   VP                  e.   V;P                  \        VP                  W$4      ,          un
        RVn        RRR4       R#   + '       g   i     LÒ; i  + '       g   i     R# ; i  \        P                  ! 4       p\        4       p\        ;_uu_ 4        \        P                  T4      pTem   T;P
                  ^,          un        TP
                  ^ 8X  dC   TP                  e.   T;P                  \        TP                  Y$4      ,          un
        RTn        RRR4       i   + '       g   i     i ; i; i5i)aü  Mark the enclosed block as time spent blocked on a human prompt. Wrap ONLY
code that is genuinely parked waiting for a user's answer (the CLI approval
prompt, the gateway approval poll loop). The concurrent tool batch deadline
excludes this time; wrapping anything else re-creates the hang where
arbitrary wedged code pushes the deadline out forever. Overlapping windows
for the same session coalesce (pending counter), so two serialized approval
prompts don't double-count the same wall clock.

See #79719.
N)rD   rR   rS   Ú_human_wait_lockr>   r   r   r+   r7   r8   r   r2   )r4   r=   r.   r<   r/   s   &    r
   Úhuman_wait_windowrX   l   s‡  é € ô �{Ó
#€CÜ
�.Š.Ó
€Cß	Ö	Ü! #Ó&ˆØ�=‰=˜AÔØ#&Ô Ø�Š˜Õ�÷	 
ð
0Ûä�nŠnÓˆô %Ó&ˆßÖÜ&×*Ñ*¨3Ó/ˆEØÒ Ø—’ Õ"•Ø—=‘= AÔ%Ø×+Ñ+Ò7Ø×/Ò/Ô3JÈ5×K_ÑK_ÐadÓ3nÕnÕ/Ø+/�EÔ(÷ Ñ÷ 
×	ú÷ ×Ðûô	 �nŠnÓˆô %Ó&ˆßÖÜ&×*Ñ*¨3Ó/ˆEØÒ Ø—’ Õ"•Ø—=‘= AÔ%Ø×+Ñ+Ò7Ø×/Ò/Ô3JÈ5×K_ÑK_ÐadÓ3nÕnÕ/Ø+/�EÔ(÷ ××Ðÿsf   ‚/H1±<D7Á-H1Á6E Á:-H1Â'BE
Ä-
H1Ä7E	ÅH1Å
E	Å	H1Å.H.ÆBH	È	H.ÈH+È&H.È.H1c                ó>   € V ^8„  d   QhR\         R,          R\        /# r@   )r5   r!   )r   s   "r
   r   r   �   s   € ÷ ñ ¤C¨$¥Jð ¼%ñ r   c                ój  € \        V 4      p\        P                  ! 4       p\        4       p\        ;_uu_ 4        \
        P                  V4      pVf    RRR4       R# VP                  pVP                  e   V\        VP                  W#4      ,          pVuuRRR4       #   + '       g   i     R# ; i)uj  Return total human-wait seconds recorded for the session: completed windows
plus the currently open one (if any). Monotonically non-decreasing for the
life of the process â€” except when an idle session's entry is evicted under
cap pressure, which can only shrink a consumer's baseline delta to zero (the
safe direction: the deadline fires sooner). Deadline consumers snapshot a
baseline at batch start and use the delta. Each window's contribution is
clamped to :func:`human_wait_ceiling` (belt-and-braces against the
wedged-window hang).

Each window's contribution is clamped to :func:`human_wait_ceiling`: every legitimate human wait
self-terminates at ``approvals.timeout`` (both the CLI prompt join and the gateway poll loop enforce
it), so a window that overstays that bound is itself wedged and must not keep extending a batch deadline
(belt-and-braces for #79719).
Nr   )
rD   rR   rS   r+   rW   r7   r8   r   r   r2   )r4   r=   r.   r/   r<   Útotals   &     r
   Úhuman_wait_secondsr\   �   s‹   € ô �{Ó
#€CÜ
�.Š.Ó
€Cä Ó"€Gß	Ö	Ü"×&Ñ& sÓ+ˆØŠ=Ø÷ 
Ñ	ð ×'Ñ'ˆØ×ÑÒ+ØÔ,¨U×-AÑ-AÀ3ÓPÕPˆEØ÷ 
×	×	Ó	ús   ¹B!Á8B!Â!B2	c                ób   € V ^8„  d   Qh/ ^ \         9   d   \        \        \        3,          ;R&   # )r   r7   )Ú__conditional_annotations__Údictr5   r   )r   s   "r
   r   r      s*   € ÷ ñ ÷N 4Ò 3”DœœoÐ-Õ.Ñ 3òOr   rJ   )r^   Ú__doc__Ú
contextlibÚ	threadingrR   r   ÚLockrW   r7   r;   r*   r+   r2   r>   rD   rT   ÚcontextmanagerrX   r\   r   )r^   s   @r
   Ú<module>re      s…   øðöó Û Û ÷%ñ %ð —>’>Ó#Ð Ø13Ð Ó 3ØÐ ð Ð õjõ1õõ$6õ
7ð ×Ñö 0ó ð 0÷Fñ r   