+
    lHàj’(  ã                  óÊ  a € 0 t $ R t^ RIHt ^ RIt^ RIt^ RIHt ^ RIH	t	H
t
 ^ RIHt ^ RIHtHtHtHt ]P$                  ! ]4      t]	! RRR7      tR	]R
&   RRRRRRR^RRRRRRRRRRRRRR/tR]R&    ! R R]4      t ! R R ]]]3,          4      tR! R" ltR# R$ ltR% R& ltR' R( lt R8R) R* llt!R+ R, lt"R- R. lt#R/R/R0 R1 llt$R2 R3 lt%R/R/R4 R5 llt&]R6 R7 l4       t'R# )9ah  Per-turn terminal scope: profile-scoped TERMINAL_* policy.

Multiplexed surfaces (gateway, dashboard/TUI, cron) serve several profiles from one process;
mirroring terminal settings into ``os.environ`` let the first profile pin its backend onto
everyone else (sandbox escape). Like ``agent/secret_scope.py``, a ContextVar holds the active
profile's COMPLETE ``TERMINAL_*`` policy; while bound, ``terminal_env`` resolves ONLY from it
(omitted keys -> defined default, never ambient env). If the policy cannot be resolved a
*refusal* scope is installed and terminal execution raises :class:`TerminalPolicyUnavailable`.
)ÚannotationsN)Úcontextmanager)Ú
ContextVarÚToken)ÚPath)ÚAnyÚDictÚIteratorÚOptionalÚhermes_terminal_scope)Údefaultr   Ú_terminal_scope_varÚcwdÚ.Ússh_hostÚ Ússh_userÚssh_portÚssh_keyÚdocker_orphan_reaperTÚdocker_persist_across_processesÚsandbox_dirÚlifetime_secondsi,  Údocker_shared_container_keyÚ	home_modeÚautoúdict[str, Any]Ú_TOOL_LEVEL_DEFAULTSc                  ó   € ] tR t^#tRtRtR# )ÚTerminalPolicyUnavailablezOThe routed profile's ``.env``/``config.yaml`` exists but cannot be read/parsed.© N)Ú__name__Ú
__module__Ú__qualname__Ú__firstlineno__Ú__doc__Ú__static_attributes__r    ó    Ú9/home/ubuntu/.hermes/hermes-agent/tools/terminal_scope.pyr   r   #   s   † ÝYr'   r   c                  ó2   a € ] tR t^'tRtR V 3R lltRtV ;t# )ÚTerminalPolicyRefusalzKMarker scope (empty dict subclass) installed when policy resolution failed.c               ó    € V ^8„  d   QhRRRR/# )é   ÚreasonÚstrÚreturnÚNoner    )Úformats   "r(   Ú__annotate__Ú"TerminalPolicyRefusal.__annotate__*   s   € ÷ ñ ˜sð  tñ r'   c                	ó0   <€ \         SV `  4        Wn        R # ©N)ÚsuperÚ__init__r-   )Úselfr-   Ú	__class__s   &&€r(   r7   ÚTerminalPolicyRefusal.__init__*   s   ø€ Ü‰ÑÔØŽr'   )r-   )r!   r"   r#   r$   r%   r7   r&   Ú__classcell__)r9   s   @r(   r*   r*   '   s   ø† ÙU÷ö r'   r*   c               ó    € V ^8„  d   QhRRRR/# )r,   ÚmappingúOptional[dict[str, str]]r/   r   r    )r1   s   "r(   r2   r2   /   s   € ÷ ,ñ ,Ð 8ð ,¸Uñ ,r'   c                ó,   € \         P                  V 4      # )z;Install *mapping* as the current context's terminal policy.)r   Úset)r=   s   &r(   Úset_terminal_scoperA   /   s   € ä×"Ñ" 7Ó+Ð+r'   c               ó    € V ^8„  d   QhRRRR/# )r,   Útokenr   r/   r0   r    )r1   s   "r(   r2   r2   4   s   € ÷ %ñ % ð %¨$ñ %r'   c                ó0   € \         P                  V 4       R # r5   )r   Úreset)rC   s   &r(   Úreset_terminal_scoperF   4   s   € Ü×Ñ˜eÖ$r'   c               ó   € V ^8„  d   QhRR/# )r,   r/   r>   r    )r1   s   "r(   r2   r2   8   s   € ÷ %ñ %Ð4ñ %r'   c                 ó*   € \         P                  4       # )zEThe active scope mapping/refusal, or ``None`` when no scope is bound.)r   Úgetr    r'   r(   Úget_terminal_scoperJ   8   s   € ä×"Ñ"Ó$Ð$r'   c               ó   € V ^8„  d   QhRR/# )r,   r/   r0   r    )r1   s   "r(   r2   r2   =   s   € ÷ Mñ M˜Dñ Mr'   c                 óŠ   € \         P                  4       p \        V \        4      '       d   \	        RV P
                   24      hR# )a   Raise when the active scope is a refusal scope (fail closed).

Execution paths (terminal tool, execute_code) call this before spawning anything: under a refusal scope
the profile's terminal policy could not be resolved, and running with the launch process's ambient
policy is exactly the authority leak this module closes (#68559 requires refusal, not fallback).
Non-scoped and policy-scoped contexts pass silently.
z.terminal policy unavailable for this profile: N)r   rI   Ú
isinstancer*   r   r-   )Úscopes    r(   Úenforce_no_refusalrO   =   sB   € ô  ×#Ñ#Ó%€EÜ�%Ô.×/Ò/Ü'Ø<¸U¿\¹\¸NÐKóMð 	Mñ 0r'   c               ó$   € V ^8„  d   QhRRRRRR/# )r,   Únamer.   r   r/   r    )r1   s   "r(   r2   r2   K   s!   € ÷ 4ñ 4�sð 4 Sð 4°#ñ 4r'   c                óÈ   € \         P                  4       pVf    \        P                  P                  W4      # \	        4        VP                  V 4      pVf   V# \        V4      # )zÀAuthoritative read of a ``TERMINAL_*`` variable.

No scope: process env, then *default*. Refusal scope: raise. Policy scope: ONLY the
policy; a missing key yields *default*, never os.environ.
)r   rI   ÚosÚenvironrO   r.   )rQ   r   rN   Úvalues   &&  r(   Úterminal_envrV   K   sP   € ô  ×#Ñ#Ó%€EØ‚}Ü�z‰z�~‰~˜dÓ,Ð,ÜÔØ�I‰I�d‹O€EØ’mˆ7Ð3¬¨U«Ð3r'   c               ó$   € V ^8„  d   QhRRRRRR/# )r,   rN   údict[str, str]r=   r   r/   r0   r    )r1   s   "r(   r2   r2   Y   s!   € ÷ 8ñ 8 >ð 8¸Nð 8Ètñ 8r'   c                óè   € ^ RI HpHp VP                  4        FU  w  rEVe&   VR8X  d!   \	        V4      P                  4       R9   d   K0  VP                  V4      pV'       g   KK  V! V4      W&   KW  	  R# )é    )ÚTERMINAL_CONFIG_ENV_MAPÚ_terminal_env_valueNr   >   r   r   r   )Úhermes_cli.configr[   r\   Úitemsr.   ÚstriprI   )rN   r=   r[   r\   Úcfg_keyrU   Úenv_vars   &&     r(   Ú_apply_terminal_mappingrb   Y   s^   € ßNà!Ÿ-™-ž/‰ˆàŠ=˜W¨Ô-´#°e³*×2BÑ2BÓ2DÐH\Ô2\ÙØ)×-Ñ-¨gÓ6ˆß‰7ñ 1°Ó7ˆE‹Nó *r'   c               ó   € V ^8„  d   QhRR/# )r,   r/   rX   r    )r1   s   "r(   r2   r2   h   s   € ÷ ñ  ñ r'   c                 óp   € ^ RI Hp  / p\        T/ \        CV P	                  R4      ;'       g    / C4       V# )z¯Code-default ``TERMINAL_*`` policy: what a profile-bypassing session freezes instead of
reading ``.env``/``config.yaml``. Total like the profile projection, minus the profile.)ÚDEFAULT_CONFIGÚterminal)Úhermes_cli.config_defaultsre   rb   r   rI   )re   rN   s     r(   Údefault_terminal_scoperh   h   s<   € õ :à€EÜ˜EÐ#eÔ&:Ð#e¸~×?QÑ?QÐR\Ó?]×?cÐ?cÐacÐ#eÔfØ€Lr'   Úenv_overlayc               ó$   € V ^8„  d   QhRRRRRR/# )r,   Úhermes_homer   ri   r>   r/   rX   r    )r1   s   "r(   r2   r2   r   s)   € ÷ Añ AØðAØ&>ðAØJXñAr'   c               óà  € \        V 4      p\        4       pVP                  R4      pVR,          pVP                  4       '       dL    VP	                  4        ^ RIHp T! T4      pTP                  R TP                  4        4       4       RT9   p	MRp	V'       dA   VP                  R	 VP                  4        4       4       VP                  R4      RV39  d   R
p	VR,          p
 V
P                  4       pT'       di   ^ RIHp  T! T
4      p\        T\        4      '       d   TP                  R4      MRp\        T\        4      '       d   \        Y>4       T	;'       g    RT9   p	T	'       d   RMRTR&   \!        T4       T#   \
         d   p\        RT RT 24      ThRp?ii ; i  \
         d   p\        RT RT 24      ThRp?ii ; i  \
         d   p\        RT
 RT 24      ThRp?ii ; i)uP  Build the COMPLETE effective ``TERMINAL_*`` policy for a profile home.

Projection: ``DEFAULT_CONFIG['terminal']`` <- profile ``.env`` TERMINAL_* <- *env_overlay*
<- profile ``config.yaml`` ``terminal:``. Total by construction, so a bound scope never
widens back to ambient authority. Raises :class:`TerminalPolicyUnavailable` if a present
file is unreadable.

*env_overlay* is a TRUSTED ``TERMINAL_*`` mapping captured from the launch process before
multiplexing began (``tui_gateway/launch_profile_policy.py``): the launch profile's
env-only policy (``TERMINAL_ENV=ssh`` from systemd, ``op run``, a launcher bridge) has no
file to rebuild it from, and reading live ``os.environ`` here is the leak this module
closes. It sits where the process env sits in the standalone bridge â€” explicit YAML keys
still win (``apply_terminal_config_to_env``).
ÚTERMINAL_DOCKER_IMAGEz.envzcannot read z: N)Úload_env_filec              3  ór   "  € T F-  w  rVP                  R 4      '       g   K  V\        V4      3x € K/  	  R# 5i)Ú	TERMINAL_N©Ú
startswithr.   ©Ú.0ÚkÚvs   &  r(   Ú	<genexpr>Ú/build_profile_terminal_scope.<locals>.<genexpr>�   s*   é € Ð^Ñ-@¡T QÀAÇLÁLÐQ\×D]”[�aœ˜Q›•[Ó-@ùs   ‚7¢7Fc              3  ó„   "  € T F6  w  rVP                  R 4      '       g   K  VR8w  g   K'  V\        V4      3x € K8  	  R# 5i)rp   ÚTERMINAL_DOCKER_IMAGE_PINNEDNrq   rs   s   &  r(   rw   rx   —   s>   é € ð [Ñ-@¡T QØŸ™ [×1ô !Ø67Ð;YÑ6Yô !�aœ˜Q›•[Ó-@ùs   ‚A ¢A «A Tzconfig.yamlz"cannot resolve terminal config in )Úload_yaml_file_readonlyzcannot parse rf   Údocker_imageÚ1Ú0rz   )r   rh   rI   ÚexistsÚ
read_bytesÚ	Exceptionr   Úagent.secret_scopern   Úupdater^   Úutilsr{   rM   Údictrb   Ú_resolve_scope_cwd_placeholder)rk   ri   ÚhomerN   Údefault_imageÚenv_pathÚexcrn   Úprofile_envÚimage_pinnedÚconfig_pathÚconfig_existsr{   ÚrawÚraw_terminals   &$             r(   Úbuild_profile_terminal_scoper‘   r   sö  € ô  �Ó€DÜ"Ó$€EØ—I‘IÐ5Ó6€MØ�f�}€HØ‡�×Òð	WØ×ÑÔ!õ 	5á# HÓ-ˆØ�‰Ñ^¨[×->Ñ->Ô-@Ó^Ô^ð /°+Ñ=‰àˆßØ�‰ñ [¨[×->Ñ->Ô-@ó [ô 	[ð
 �?‰?Ð2Ó3¸DÀ-Ð;PÔPØˆLð ˜Õ&€KðeØ#×*Ñ*Ó,ˆ÷ Ý1ð	[á)¨+Ó6ˆCô /9¸¼d×.CÒ.C�s—w‘w˜zÔ*ÈˆÜ�l¤D×)Ò)Ü# EÔ8Ø'×IÐI¨>¸\Ñ+IˆLß3?©CÀS€EÐ
(Ñ)Ü" 5Ô)Ø€LøôQ ô 	WÜ+¨l¸8¸*ÀBÀsÀeÐ,LÓMÐSVÐVûð	Wûô0 ô eÜ'Ð*LÈTÈFÐRTÐUXÐTYÐ(ZÓ[ÐadÐdûðeûô ô 	[Ü+¨m¸K¸=ÈÈ3È%Ð,PÓQÐWZÐZûð	[úsH   ÁF Ã%F& ÄG ÆF#ÆFÆF#Æ&GÆ1GÇGÇG-ÇG(Ç(G-c               ó    € V ^8„  d   QhRRRR/# )r,   rN   rX   r/   r0   r    )r1   s   "r(   r2   r2   ¶   s   € ÷ )ñ )¨.ð )¸Tñ )r'   c           
     ó0  € V P                  R4      '       d   R# ^ RIHp V! RV P                  RR4      RV P                  RR4      P                  4       P	                  4       R	9   \        \        P                  ! 4       4      R7      pV'       d   W R&   R# R# )
u  Give a scope with no explicit ``terminal.cwd`` the same resolved ``TERMINAL_CWD`` a standalone
gateway computes at import (``gateway/run.py``: local backend â†’ ``$HOME``; docker with the
workspace mount â†’ the host cwd signal; other backends â†’ unset). Without it a routed turn's
``resolve_agent_cwd()`` falls back to the multiplexer PROCESS cwd (wherever ``hermes gateway``
was launched), so the system prompt, context-file discovery and the local terminal all run in
a directory the profile's standalone gateway would never have used.ÚTERMINAL_CWDN)Ú resolve_placeholder_terminal_cwdr   ÚTERMINAL_ENVÚ&TERMINAL_DOCKER_MOUNT_CWD_TO_WORKSPACEÚfalse)Úconfigured_cwdÚterminal_backendÚmessaging_cwdÚdocker_mount_cwd_to_workspaceÚhome_fallback>   r}   ÚyesÚtrue)rI   Úgateway.cwd_placeholderr•   r_   Úlowerr.   r   r‡   )rN   r•   Úresolveds   &  r(   r†   r†   ¶   s}   € ð ‡y�y�× Ò ÙÝHá/Ø¨E¯I©I°nÀbÓ,IØØ&+§i¡iØ4°gó'?ß?D¹u»wÇuÁuÃwÐRfñ'gäœ$Ÿ)š)›+Ó&ô€H÷ Ø (ˆnÓñ r'   c               ó$   € V ^8„  d   QhRRRRRR/# )r,   rk   r   ri   r>   r/   r   r    )r1   s   "r(   r2   r2   Ì   s)   € ÷ Hñ HØðHØ&>ðHØJOñHr'   c               óØ   €  \        \        WR7      4      #   \         dH   p\        P	                  RT4       \
        P                  \        \        T4      4      4      u Rp?# Rp?ii ; i)zYBuild AND install a profile's policy; on failure install the refusal scope. Never raises.)ri   zterminal policy unavailable: %sN)	rA   r‘   r   ÚloggerÚwarningr   r@   r*   r.   )rk   ri   rŠ   s   &$ r(   Úinstall_profile_terminal_scoper§   Ì   sU   € ðHÜ!Ô">¸{Ô"dÓeÐeøÜ$ô HÜ�‰Ð8¸#Ô>Ü"×&Ñ&Ô'<¼SÀ»XÓ'FÓGÕGûðHús   ‚ —A)¢<A$ÁA)Á$A)c               ó    € V ^8„  d   QhRRRR/# )r,   rk   r   r/   zIterator[None]r    )r1   s   "r(   r2   r2   ×   s   € ÷ $ñ $¸#ð $À.ñ $r'   c              #  ód   "  € \        V 4      p Rx € \        V4       R#   \        T4       i ; i5i)zLInstall the profile's terminal policy for a bounded turn/fire. Never raises.N)r§   rF   )rk   rC   s   & r(   Ú(install_and_reset_profile_terminal_scoperª   Ö   s*   é € ô +¨;Ó7€Eð$Ûä˜UÖ#øÔ˜UÕ#üs   ‚0�  “0 -­0)r   )(Ú__conditional_annotations__r%   Ú
__future__r   ÚloggingrS   Ú
contextlibr   Úcontextvarsr   r   Úpathlibr   Útypingr   r   r	   r
   Ú	getLoggerr!   r¥   r   Ú__annotations__r   r�   r   r…   r.   r*   rA   rF   rJ   rO   rV   rb   rh   r‘   r†   r§   rª   )r«   s   @r(   Ú<module>r´      s  øðôõ #ã Û 	Ý %ß )Ý ß 0Ó 0à	×	Ò	˜8Ó	$€ñ #-Ð-DÈdÔ"SÐ �ZÓ Sð
 
ˆ3�
˜B 
¨B°
¸BÀ	È2Ø˜DÐ"CÀTØ�2Ð)¨3Ð0MÈrØ�ð	(Ð �nó ôZ 	ô Zô˜D  c �Nô õ,õ
%õ%õ
M÷4õ8õðAØAE÷AõH)ð,HØAE÷Hð ô$ó ò$r'   