Ë
    ãß²jm<  ã            	       ó  — U d Z ddlZddlZddlZddlmZ ddlmZmZ  ej                  d«      Z
dAdedd	d
ej                  fd„Z ed«      Zej                  e   ed<    ed«      Zej                  e   ed<    ed«      Zej                  e   ed<    ed«      Zej                  e   ed<    edd«      Zej                  edz     ed<   ded
ej*                  fd„Zdej*                  d
dfd„Zd
efd„Zded
dfd„Zded
ej*                  e   fd„Zdej*                  e   d
dfd„Zeej*                  e   ej*                  e   ej*                  e   f   Zdddd œd!ed"ed#ed
efd$„Zd%ed
dfd&„ZdBded
efd'„Z ded
efd(„Z!d
efd)„Z"d
efd*„Z# e$h d+£«      Z%d
efd,„Z&d
efd-„Z'd
efd.„Z(dCd/„Z)d0ed
efd1„Z*d2Z+d
efd3„Z,d
e-fd4„Z.d
efd5„Z/d
e0fd6„Z1d7e0d
efd8„Z2d
e-fd9„Z3d:ed
efd;„Z4d
efd<„Z5d
efd=„Z6d
efd>„Z7d
efd?„Z8d
eeedz  f   fd@„Z9y)DzûApproval context: who is asking, from where, under which policy.

Session identity and observability contextvars, the interactive/gateway/cron/
unattended predicates, and the ``approvals.*`` config readers used by every
gate in :mod:`tools.approval`.
é    N)Úcfg_get)Úenv_var_enabledÚis_truthy_valueztools.approvalÚ ÚnameÚdefaultz
str | NoneÚreturnc                 ó0   — t        j                  | |¬«      S )N©r   )ÚcontextvarsÚ
ContextVar)r   r   s     ú;/home/ubuntu/.hermes/hermes-agent/tools/approval_context.pyÚ_ctxr      s   € Ü×!Ñ! $°Ô8Ð8ó    Úapproval_session_keyÚ_approval_session_keyÚapproval_turn_idÚ_approval_turn_idÚapproval_tool_call_idÚ_approval_tool_call_idÚapproval_session_idÚ_approval_session_idÚhermes_interactiveÚ_hermes_interactive_ctxÚinteractivec                 ó<   — t         j                  | rd«      S d«      S )zMBind interactive mode for the current context instead of mutating os.environ.Ú1r   )r   Úset)r   s    r   Úset_hermes_interactive_contextr   %   s   € ä"×&Ñ&©k sÓBÐB¸rÓBÐBr   Útokenc                 ó.   — t         j                  | «       y)zDRestore the prior value from :func:`set_hermes_interactive_context`.N)r   Úreset©r    s    r   Ú reset_hermes_interactive_contextr$   *   s   € ä×!Ñ! %Õ(r   c                  óZ   — t         j                  «       } | �t        | «      S t        d«      S )zITrue for an interactive CLI/ACP session (contextvar first, env fallback).ÚHERMES_INTERACTIVE)r   Úgetr   r   )Úctx_vals    r   Ú_is_interactive_clir)   /   s+   € ä%×)Ñ)Ó+€GØ'.Ð':Œ?˜7Ó#ÐeÄÐPdÓ@eÐer   Ú	hook_namec                 óž  — 	 ddl m} 	 |j                  dt        j                  «       «       |j                  dt        j                  «       «       t        j                  «       r$|j                  dt        j                  «       «        || fi |¤Ž y# t        $ r Y yw xY w# t        $ r!}t        j                  d| |«       Y d}~yd}~ww xY w)a  Invoke a plugin lifecycle hook (pre_approval_request / post_approval_response).

    Lazy-imports the plugin manager (approval.py is imported long before plugins
    are discovered). Never raises: approval flow is safety-critical, plugin
    observability is not.
    r   )Úinvoke_hookNÚturn_idÚtool_call_idÚ
session_idz$Approval hook %s dispatch failed: %s)
Úhermes_cli.lifecycler,   Ú	ExceptionÚ
setdefaultr   r'   r   r   ÚloggerÚdebug)r*   Úkwargsr,   Úexcs       r   Ú_fire_approval_hookr7   5   s²   € ðÝ4ðMØ×Ñ˜)Ô%6×%:Ñ%:Ó%<Ô=Ø×Ñ˜.Ô*@×*DÑ*DÓ*FÔGÜ×#Ñ#Ô%Ø×Ñ˜lÔ,@×,DÑ,DÓ,FÔGÙ�IÑ( Ó(øô ò Ùðûô ò Mä�‰Ð;¸YÈ×LÑLûðMús)   ‚B ‰B	B" Â	BÂBÂ"	CÂ+CÃCÚsession_keyc                 ó4   — t         j                  | xs d«      S )z<Bind the active approval session key to the current context.r   )r   r   )r8   s    r   Úset_current_session_keyr:   K   s   € ä ×$Ñ$ [Ò%6°BÓ7Ð7r   c                 ó.   — t         j                  | «       y)z/Restore the prior approval session key context.N)r   r"   r#   s    r   Úreset_current_session_keyr<   P   s   € ä×Ñ Õ&r   ©r-   r.   r/   r-   r.   r/   c                 ó–   — t         j                  | xs d«      t        j                  |xs d«      t        j                  |xs d«      fS )z3Bind active tool correlation IDs to approval hooks.r   )r   r   r   r   r=   s      r   Ú!set_current_observability_contextr?   X   sE   € ä×!Ñ! '¢-¨RÓ0Ô2H×2LÑ2LÈ\ÒM_Ð]_Ó2`Ü ×$Ñ$ ZÒ%5°2Ó6ð8ð 8r   Útokensc                 óŽ   — | \  }}}t         j                  |«       t        j                  |«       t        j                  |«       y)z,Restore prior approval hook correlation IDs.N)r   r"   r   r   )r@   Ú
turn_tokenÚ
tool_tokenÚsession_tokens       r   Ú#reset_current_observability_contextrE   ^   s8   € à,2Ñ)€J�
˜MÜ×Ñ˜}Ô-Ü× Ñ  Ô,Ü×Ñ˜JÕ'r   c                 óP   — t         j                  «       x}r|S ddlm}  |d| «      S )uV   Return the active session key: approval contextvar â†’ session_context â†’ os.environ.r   ©Úget_session_envÚHERMES_SESSION_KEY)r   r'   Úgateway.session_contextrH   )r   r8   rH   s      r   Úget_current_session_keyrK   f   s-   € ä+×/Ñ/Ó1Ð1€{Ð1ØÐÝ7ÙÐ/°Ó9Ð9r   c                 ó|   — 	 ddl m}  || d«      xs dS # t        $ r t        j                  | d«      xs dcY S w xY w)zÈSession-scoped env value, contextvar-first so one cron/-q job cannot taint
    unrelated gateway/API/TUI turns in the same process; process env is the
    fallback for CLI tests and older entrypoints.r   rG   r   )rJ   rH   r1   ÚosÚgetenv)r   rH   s     r   Ú_session_envrO   n   sA   € ð)Ý;Ù˜t RÓ(Ò.¨BÐ.øÜò )Ü�y‰y˜˜rÓ"Ò( bÒ(ð)ús   ‚ •#;º;c                  ó   — t        d«      S )zBReturn the current gateway platform from contextvars/env fallback.ÚHERMES_SESSION_PLATFORM)rO   © r   r   Ú_get_session_platformrS   y   s   € äÐ1Ó2Ð2r   c                  ó*   — t        t        d«      «      S )z?True when the current approval decision is running inside cron.ÚHERMES_CRON_SESSION©r   rO   rR   r   r   Ú_is_cron_approval_contextrW   ~   s   € äœ<Ð(=Ó>Ó?Ð?r   >   ÚwebhookÚ
api_serverÚmsgraph_webhookc                  ó"   — t        «       t        v S )u¤  True when the session platform is a programmatic/unattended surface.

    Webhook, msgraph_webhook, and api_server sessions bind ``HERMES_SESSION_PLATFORM`` like chat gateways
    do, but there is no human who can resolve a pending approval. Treating them as gateway approval contexts
    blocks the session for the full approval timeout (60-300s) and then fails closed anyway â€” the deadlock
    in #37284/#87509.
    )rS   Ú_UNATTENDED_APPROVAL_PLATFORMSrR   r   r   Ú(_is_unattended_platform_approval_contextr]   ‰   s   € ô !Ó"Ô&DÐDÐDr   c                  ó*   — t        t        d«      «      S )as  True for a single-query (-q) session: ``hermes chat -q`` exports
    ``HERMES_INTERACTIVE=1`` (so sudo password prompts work) but nobody is waiting
    to answer approvals; without this marker the gate would wait the full timeout,
    fail closed and push the agent toward workarounds (e.g. execute_code).
    ``approvals.single_query_mode`` makes the path deterministic.ÚHERMES_SINGLE_QUERY_SESSIONrV   rR   r   r   Ú!_is_single_query_approval_contextr`   ”   s   € ô œ<Ð(EÓFÓGÐGr   c                  ól   — t        «       s
t        «       ryt        d«      xs t        t	        «       «      S )a©  True inside a gateway/API session that can answer an approval.

    Legacy integrations set HERMES_GATEWAY_SESSION; concurrent paths bind
    HERMES_SESSION_PLATFORM via contextvars. Cron is NEVER a gateway approval
    context even when it originated from a platform (cron binds the platform for
    delivery routing): falling through would submit a pending approval with no
    listener and block the job indefinitely; unattended platforms likewise.

    Unattended programmatic platforms (webhook, msgraph_webhook, api_server) are excluded for the same
    reason: those adapters have no ``send_exec_approval`` and no way to receive ``/approve`` replies.
    Submitting a pending approval there blocks the session for the full approval timeout (60-300 s) with no
    human who can resolve it (#37284, 87509). Their dangerous-command handling is governed by
    ``approvals.unattended_mode`` config (default deny), mirroring cron.
    FÚHERMES_GATEWAY_SESSION)rW   r]   r   ÚboolrS   rR   r   r   Ú_is_gateway_approval_contextrd   �   s-   € ô !Ô"Ô&NÔ&PØÜÐ3Ó4ÒU¼Ô=RÓ=TÓ8UÐUr   c                 óD   — | �| S 	 ddl m}  |«       S # t        $ r Y yw xY w)zXExplicit callback, else the per-thread one from ``terminal_tool.set_approval_callback``.Nr   )Ú_get_approval_callback)Útools.terminal_toolrf   r1   )Úapproval_callbackrf   s     r   Ú_resolve_cli_approval_callbackri   ±   s1   € àÐ$Ø Ð ðÝ>Ù%Ó'Ð'øÜò Ùðús   † “	žÚis_clic                 ó0   — t        | xr
 |duxr |du «      S )ak  Prefer the CLI Dangerous Command panel over a silent pending approval:
    ``HERMES_EXEC_ASK`` (or a platform marker) can leak into an interactive CLI
    process (historically via ``import gateway.run``), and without a gateway notify
    listener the ask branch used to return ``pending_approval`` immediately and
    skip the panel the user can actually answer.N)rc   )rj   rh   Ú	notify_cbs      r   Ú$_should_fall_through_to_cli_approvalrm   ¼   s$   € ô �ÒNÐ,°DÐ8ÒN¸YÈ$Ð=NÓOÐOr   )ÚmanualÚsmartÚoffc                 óü   — t        | t        «      r| du rdS dS t        | t        «      rT| j                  «       j	                  «       }|t
        v r|S |r*t        j                  d| dj                  t
        «      «       y)a!  Normalize approval mode values loaded from YAML/config. YAML 1.1 parses a
    bare ``off`` as False, so ``mode: off`` arrives as a bool; treat it as the
    intended string mode. Unknown strings (e.g. 'auto') warn and fall back to
    'manual' instead of silently failing every mode check.Frp   rn   uF   Unknown approvals.mode %r â€” defaulting to 'manual'. Valid values: %sz, )	Ú
isinstancerc   ÚstrÚstripÚlowerÚ_VALID_MODESr3   ÚwarningÚjoin)ÚmodeÚ
normalizeds     r   Ú_normalize_approval_moder{   È   sq   € ô
 �$œÔØ ™ˆuÐ3¨8Ð3Ü�$œÔØ—Z‘Z“\×'Ñ'Ó)ˆ
ØœÑ%ØÐÙÜ�N‰Nð .Ø/3°T·Y±Y¼|Ó5LôNàr   c                  ó¢   — 	 ddl m}   | «       j                  di «      xs i S # t        $ r"}t        j                  d|«       i cY d}~S d}~ww xY w)u›   Read the approvals config block: the LIVE config-cache sub-dict
    (load_config_readonly contract) â€” callers must not mutate it or any nested structure.r   ©Úload_config_readonlyÚ	approvalsz"Failed to load approval config: %sN)Úhermes_cli.configr~   r'   r1   r3   rw   )r~   Úes     r   Ú_get_approval_configr‚   Ù   sI   € ðÝ:Ù#Ó%×)Ñ)¨+°rÓ:Ò@¸bÐ@øÜò Ü�‰Ð;¸QÔ?Ø�	ûðús   ‚ # £	A¬A	ÁAÁ	Ac                  ó    — 	 ddl m}   | «       x}�|j                  S 	 t	        t        «       j                  dd«      «      S # t        $ r Y Œ.w xY w)zKReturn 'manual', 'smart', or 'off' (a hosted-room policy overrides config).r   )Úcurrent_room_execution_policyry   rn   )Ú$gateway.hosted_room_execution_policyr„   Úapproval_moder1   r{   r‚   r'   )r„   Úroom_policys     r   Ú_get_approval_moderˆ   ä   s\   € ðÝVÙ8Ó:Ð:ˆKÐGØ×,Ñ,Ð,ð Hô $Ô$8Ó$:×$>Ñ$>¸vÀxÓ$PÓQÐQøô ò Ùðús   ‚A Á	AÁAc                  ó
  — 	 t        t        «       j                  dd«      «      } 	 ddlm} t        |«      }| |kD  rt        j                  d| |«       t        | |«      S # t        t        f$ r Y yw xY w# t        $ r d}Y ŒJw xY w)a³  Read ``approvals.timeout`` (default 300s: gateway push notifications may
    not be seen for minutes; 60s failed closed before Telegram taps landed).
    Clamped to ``agent.deadline.MAX_SAFE_TIMEOUT_S`` (~1 year): a larger value
    overflows ``time_t`` inside ``Thread.join`` / ``Lock.acquire`` on macOS and
    crashed every parallel tool batch; clamping at the single config-read site
    keeps every consumer platform-safe at once.Útimeouti,  r   )ÚMAX_SAFE_TIMEOUT_Si€3ázGapprovals.timeout=%s exceeds the platform-safe maximum; clamping to %ss)Úintr‚   r'   Ú
ValueErrorÚ	TypeErrorÚagent.deadliner‹   r1   r3   rw   Úmin)Úrawr‹   Úsafe_caps      r   Ú_get_approval_timeoutr“   ï   sŒ   € ðÜÔ&Ó(×,Ñ,¨Y¸Ó<Ó=ˆð#Ý5ÜÐ)Ó*ˆð ˆX‚~Ü�‰Ð`ÐbeÐgoÔpÜˆs�HÓÐøô œ	Ð"ò Ùðûô
 ò #Ø"Šð#ús"   ‚#A ¦A4 ÁA1Á0A1Á4BÂBÚsecondsc                 ó¬   — t        t        | xs d«      d«      } | r| dz  dk(  r| dz  d}}n| r| dz  dk(  r| dz  d}}n| d}}|dk(  r|› d|› �S |› d|› d	�S )
uó   The ONE human wording for an approval timeout window, shared by the CLI timeout notice,
    the tool result's ``user_summary`` and the gateway card copy so every surface agrees:
    300 â†’ "5 minutes", 90 â†’ "90 seconds", 7200 â†’ "2 hours".r   i  Úhouré<   ÚminuteÚsecondé   Ú Ús)ÚmaxrŒ   )r”   ÚcountÚunits      r   Úformat_approval_windowr      s�   € ô ”#�g’l Ó# QÓ'€GÙ�7˜T‘> QÒ&Ø ‘o vˆt‰Ù	�W˜r‘\ QÒ&Ø ‘m Xˆt‰à˜xˆtˆØ %¨¢
ˆeˆW�A�d�VÐÐB°5°'¸¸4¸&ÀÐ0BÐBr   c                  ó8   — t        «       } t        | «      | dz  dœS )zË``{waited, suggested}`` for the ``approval.timeout`` copy: how long we waited (``5 minutes`` /
    ``90 seconds``) and a tripled ``approvals.timeout`` value the user can paste into ``hermes config set``.é   )ÚwaitedÚ	suggested)r“   r    )r”   s    r   Úapproval_timeout_notice_kwargsr¥     s    € ô $Ó%€GÜ,¨WÓ5ÀGÈaÁKÑPÐPr   Úkeyc                 ó°   — 	 ddl m} t        t         |«       d| d¬«      «      j	                  «       j                  «       }|dv rdS dS # t        $ r Y yw xY w)z?Read ``approvals.<key>`` as 'approve' or 'deny' (default deny).r   r}   r   Údenyr   >   rp   ÚyesÚallowÚapprover«   )r€   r~   rs   r   ru   rt   r1   )r¦   r~   ry   s      r   Ú_binary_approval_moder¬     sZ   € ðÝ:Ü”7Ñ/Ó1°;ÀÈVÔTÓU×[Ñ[Ó]×cÑcÓeˆØ Ð$FÑFˆyÐRÈFÐRøÜò Ùðús   ‚AA	 ÁA	 Á		AÁAc                  ó   — t        d«      S )zERead the cron approval mode from config. Returns 'deny' or 'approve'.Ú	cron_mode©r¬   rR   r   r   Ú_get_cron_approval_moder°   #  s   € ä  Ó-Ð-r   c                  ó   — t        d«      S )zRRead the single-query (-q) approval mode from config. Returns 'deny' or 'approve'.Úsingle_query_moder¯   rR   r   r   Ú_get_single_query_approval_moder³   (  s   € ä Ð!4Ó5Ð5r   c                  ó   — t        d«      S )uÀ   Approval mode for webhook / msgraph_webhook / api_server sessions; default
    deny â€” an unattended session never silently runs a flagged action unless the
    operator explicitly trusts it.Úunattended_moder¯   rR   r   r   Ú_get_unattended_approval_moder¶   -  s   € ô !Ð!2Ó3Ð3r   c                  óÊ   — 	 ddl m}   | «       xs i j                  di «      xs i }|j                  dd«      rt        |j                  dd«      «      S dS # t        $ r Y yw xY w)z¿``security.tirith_fail_open`` (default True; True when config is unreadable).
    False means the operator opted into fail-closed: an un-importable scanner
    must not silently grant access.r   r}   ÚsecurityÚtirith_enabledTÚtirith_fail_open)r€   r~   r'   rc   r1   )r~   Ú_secs     r   Ú_tirith_fail_openr¼   4  sg   € ðÝ:Ù$Ó&Ò,¨"×1Ñ1°*¸bÓAÒGÀRˆØ;?¿8¹8ÐDTÐVZÔ;[Œt�D—H‘HÐ/°Ó6Ó7ÐeÐaeÐeøÜò Ùðús   ‚AA ÁA Á	A"Á!A"c                  ó˜  — 	 ddl m}   | «       xs i j                  d«      xs i j                  d«      xs i }t        |j                  d«      xs d«      j	                  «       j                  «       }t        |j                  d«      xs d«      j	                  «       j                  «       }|xs d|dk(  rdfS d
fS # t        $ r Y y	w xY w)zCReturn explicitly selected transport and fail-closed fallback mode.r   r}   r¸   ÚapprovalÚ	transportÚbuiltinÚtransport_fallbackr   )zconfig-errorNN)r€   r~   r'   rs   rt   ru   r1   )r~   ÚcfgÚselectedÚfallbacks       r   Ú_get_approval_transport_configrÅ   @  sÄ   € ð$Ý:Ù$Ó&Ò,¨"×1Ñ1°*Ó=ÒCÀ×HÑHÈÓTÒZÐXZˆÜ�s—w‘w˜{Ó+Ò8¨yÓ9×?Ñ?ÓA×GÑGÓIˆÜ�s—w‘wÐ3Ó4Ò:¸Ó;×AÑAÓC×IÑIÓKˆð
 Ò �y¨x¸9Ò/D )ÐNÐNÈ$ÐNÐNøô	 ò $ñ $ð$ús   ‚B+B= Â=	C	ÃC	)r   r   )N):Ú__doc__r   ÚloggingrM   r€   r   Úutilsr   r   Ú	getLoggerr3   rs   r   r   r   Ú__annotations__r   r   r   r   rc   ÚTokenr   r$   r)   r7   r:   r<   ÚtupleÚ_Tokensr?   rE   rK   rO   rS   rW   Ú	frozensetr\   r]   r`   rd   ri   rm   rv   r{   Údictr‚   rˆ   rŒ   r“   r    r¥   r¬   r°   r³   r¶   r¼   rÅ   rR   r   r   ú<module>rÐ      sB  ðòó Û Û 	Ý %ß 2à	ˆ×	Ñ	Ð+Ó	,€ñ9ˆsð 9˜\ð 9°;×3IÑ3Ió 9ñ 6:Ð:PÓ5QÐ �{×-Ñ-¨cÑ2Ó QÙ15Ð6HÓ1IÐ �;×)Ñ)¨#Ñ.Ó IÙ6:Ð;RÓ6SÐ ˜×.Ñ.¨sÑ3Ó Sñ 59Ð9NÓ4OÐ �k×,Ñ,¨SÑ1Ó Oñ
 ?CÐCWÐY]Ó>^Ð ˜×/Ñ/°°d±
Ñ;Ó ^ðC°ð C¸×9JÑ9Jó Cð
)¨K×,=Ñ,=ð )À$ó )ð
f˜Tó fðM 3ð M°Tó Mð,8¨ð 8°×1BÑ1BÀ3Ñ1Gó 8ð
' [×%6Ñ%6°sÑ%;ð 'Àó 'ð
 �×!Ñ! #Ñ&¨×(9Ñ(9¸#Ñ(>À×@QÑ@QÐRUÑ@VÐVÑ
W€ð 9;ÐPRÐfhò 8°#ð 8È#ð 8Ð`cð 8Ðmtó 8ð(°ð (¸Dó (ñ: Sð :¸ó :ð)�sð )˜só )ð3˜só 3ð
@ 4ó @ñ "+Ò+WÓ!XÐ ðE°$ó EðH¨4ó HðV dó Vó(ðP°Dð PÐ[_ó Pð *€ð có ð"˜dó ðR˜Có Rð˜só ð*C Cð C¨Có CðQ¨ó Qð˜sð  só ð. ó .ð
6¨ó 6ð
4 só 4ð	˜4ó 	ðO¨¨c°3¸±:¨oÑ(>ô Or   