Ë
    ãß²j~ ã                   óÜ	  — U d Z ddlZddlZddlZddl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mZmZ  ej                  e«      Zdedefd„ZddlmZ dd	lmZmZmZmZmZmZ dd
lmZmZm Z m!Z!m"Z"m#Z#m$Z$m%Z%m&Z& ddl'm(Z(m)Z)m*Z*m+Z+m,Z, ddl-m.Z.m/Z/ dededefd„Z0 e0dde1d«      Z2 e0dde3d«      Z4 ejj                  «       Z6d„ Z7d„ Z8d„ Z9d„ Z:defd„Z;defd„Z<ddl=m>Z? dede@fd„ZAd eeef   de@fd!„ZB	 d¾d#ed$ed%e@deCfd&„ZDdd'lEmFZF d(ZGi ZHeeef   eId)<   i ZJeee3f   eId*<    ej–                  «       ZLi ZMeeej–                  f   eId+<    ej–                  «       ZNdaOd"aPd"aQ ej–                  «       ZRd,eeef   ddfd-„ZSi ZTeeeeef   f   eId.<   i ZUeeef   eId/<    ej–                  «       ZVi ZWeeef   eId0<    ej–                  «       ZXd1ee   d2ee   ddfd3„ZYd1ee   dee   fd4„ZZd1eddfd5„Z[d6ed7edee   fd8„Z\d9ed:eeef   fd;„Z]d9efd<„Z^d=ed>ee   ddfd?„Z_d9edefd@„Z` eah dA£«      Zbd9ee   de@fdB„Zc e	dC¬D«       G dE„ dF«      «       ZddedfdG„Zede@fdH„Zfd9ee   defdI„Zgd9ee   deeef   fdJ„ZhdKdLdMdNdOœZid$ed:eeef   d eeef   defdP„ZjdQed9ee   fdR„Zkd eeef   d9ee   dee   fdS„Zld"amd¿dT„ZndUe)dVœZod$edWe@depfdX„Zqdeeef   fdY„ZrdZ„ Zsd[„ Ztd\„ Zud]„ Zv ejî                  ev«       d#ede@fd^„Zxdd_lymzZzm{Z{m|Z|m}Z}m~Z~ dd`lm€Z€m�Z�m‚Z‚ ddalƒm„Z„ dbe@dce@depfdd„Z…dddeœdfee   dged1ee   d$ee   def
dh„Z†diddjœdkedle1dmee   defdn„Z‡doeˆdefdp„Z‰ G dq„ dreŠ«      Z‹e	 G ds„ dt«      «       ZŒd#ed$ed eeef   due@deŒf
dv„Z�e	 G dw„ dx«      «       ZŽdyZ�d#ed9ee   dzee1   dce@d{e@deŽfd|„Z�d}Z‘d~ede1defd€„Z’d�eŽd9ee   defd‚„Z“d#edeCfdƒ„Z”d#ed6ed�eŽd9ee   d„ee   d1edfee   d…ee   d†e@defd‡„Z•dˆZ–d#ed6ed$ed2edfee   d1eddfd‰„Z—dŠZ˜doeFd9ee   defd‹„Z™	 	 	 	 	 	 	 	 	 	 	 	 dÀd#edce@dzee1   d9ee   d„ee   due@dfee   dŒe@dbe@d�eee      d{e@dŽe1d�e1defd�„Zšde@fd‘„Z›dd’lmœZœ d“eGd”d•d–d—œd˜d™d"dšœdd›e2› dœe2› d��dždŸœd•d d—œd˜d¡d"dšœd¢d£d˜id¤d£d•id¥œgd¦œdd§d¨d©œdªœd#gd«œd¬œZ�d­„ Zž eœjî                  d“d“e�eže›d®d¯¬°«       dd±lŸm Z  ddl¡Z¢ddl£Z£ddl¤Z¤ddl¥Z¥ddl¦Z¦ddl§Z§ddl¨Z¨ddlZd²d³d´dµd¶d·d¸d¹dºd»d¼œ
Z©d½„ Zªy)Áa  Terminal tool: run shell commands in the configured backend.

Backends (``TERMINAL_ENV``): local (default), docker, singularity, modal
(direct or managed gateway), daytona, vercel_sandbox, ssh, plus
plugin-registered backends. Handles background processes, sandbox lifecycle
(per-task cache, idle reaper, atexit teardown) and sudo password plumbing.
Cloud-sandbox persistent filesystems preserve working state across sandbox
recreation but do NOT guarantee the same live sandbox or long-running
processes survive cleanup, idle reaping, or Hermes exit.

Companion modules (re-exported here, so ``tools.terminal_tool.<name>`` stays the
import/patch target): ``terminal_tool_config`` (TERMINAL_* reads, ``_quiet``),
``terminal_tool_backends`` (env builders + requirement checkers),
``terminal_tool_lifecycle`` (reaper/teardown/ensure_task_env),
``terminal_tool_sudo`` (sudo password + shell rewrites), ``terminal_tool_guards``
(pre-exec blocks), ``terminal_tool_background`` (background spawn),
``terminal_tool_result`` (foreground result post-processing).
é    N)Ú	dataclass)ÚOptionalÚDictÚAnyÚListÚvalueÚreturnc                 óH   — ddl m}  || €dd¬«      S t        | «      d¬«      S )z?Force-redact text before serializing a terminal error envelope.r   )Úredact_sensitive_textÚ T©Úforce)Úagent.redactr   Ústr)r   r   s     ú8/home/ubuntu/.hermes/hermes-agent/tools/terminal_tool.pyÚ_redact_terminal_error_textr   "   s$   € å2á  u } ÈDÔQÐQ¼#¸e»*ÈDÔQÐQó    )Ú
tool_error)Ú_check_disk_usage_warningÚ_cleanup_inactive_envsÚ_create_configured_envÚ_evict_environment_for_taskÚcleanup_all_environmentsÚensure_task_env)	Ú_is_container_backendÚ_is_host_cwdÚ_is_unusable_container_cwdÚ_parse_env_varÚ_plugin_env_flagÚ_quietÚ_safe_getcwdÚ_tenvÚ
_tenv_bool)Ú_REQUIREMENT_CHECKERSÚ_VERCEL_SANDBOX_DEFAULT_CWDÚ_check_plugin_requirementsÚ_record_unavailable_reasonÚ#terminal_backend_unavailable_reason)Úcoerce_modal_modeÚmanaged_nous_tools_enabledÚnameÚdefaultÚ
type_labelc                 ó°   — t        j                  | «      }|�|dk(  r|S 	  ||«      S # t        t        f$ r t        j                  d| |||«       |cY S w xY w)z“Parse a module-level numeric env var; a malformed value must never make
    the module unloadable at import time (CLI, ACP, tests, tool discovery).r   z;Invalid value for %s: %r (expected %s). Falling back to %r.)ÚosÚgetenvÚ	TypeErrorÚ
ValueErrorÚloggerÚwarning)r+   r,   Ú	converterr-   Úraws        r   Ú_safe_parse_import_envr7   :   sc   € ô �)‰)�D‹/€CØ
€{�c˜R’iØˆðÙ˜‹~ÐøÜ”zÐ"ò Ü�‰ØIØ�#�z 7ô	
ð Šðús    ( ¨*AÁAÚTERMINAL_MAX_FOREGROUND_TIMEOUTiX  ÚintegerÚTERMINAL_DISK_WARNING_GBg     @@Únumberc                  ó$   — t        t        dd «      S )NÚsudo_password©ÚgetattrÚ_callback_tls© r   r   Ú_get_sudo_password_callbackrB   X   s   € Ü”= /°4Ó8Ð8r   c                  ó$   — t        t        dd «      S )NÚapprovalr>   rA   r   r   Ú_get_approval_callbackrE   \   s   € Ü”= *¨dÓ3Ð3r   c                 ó   — | t         _        y)zCRegister the CLI's sudo password prompt callback (per-thread slot).N)r@   r=   ©Úcbs    r   Úset_sudo_password_callbackrI   `   s
   € à"$„MÕr   c                 ó   — | t         _        y)zJRegister the dangerous-command approval prompt callback (per-thread slot).N)r@   rD   rG   s    r   Úset_approval_callbackrK   e   s
   € à„MÕr   c                  ó    — ddl m}   | dd«      S )zŽActive gateway/WebUI session key, or "" outside sessions (ContextVar with
    the ``get_session_env`` os.environ fallback for CLI/cron/tests).r   ©Úget_session_envÚHERMES_SESSION_KEYr   ©Úgateway.session_contextrN   rM   s    r   Ú_current_session_keyrR   j   s   € õ 8áÐ/°Ó4Ð4r   c                  ó    — ddl m}   | dd«      S )ziActive session's Hermes profile name, or "" (same lookup discipline as
    :func:`_current_session_key`).r   rM   ÚHERMES_SESSION_PROFILEr   rP   rM   s    r   Ú_current_session_profilerU   r   s   € õ 8áÐ3°RÓ8Ð8r   )Úcheck_all_command_guardsÚvolume_specc                 óÆ   — t        | t        «      sy| j                  «       }t        |«      xr4 |j	                  d«      xs! t        |«      dk\  xr |d   dk(  xr |d   dv S )z>Return True when a docker volume spec bind-mounts a host path.F)ú/Ú~z./z../é   é   ú:é   )rY   ú\)Ú
isinstancer   ÚstripÚboolÚ
startswithÚlen)rW   Úvols     r   Ú_docker_volume_uses_host_pathrf      sg   € ä�k¤3Ô'ØØ
×
Ñ
Ó
€CÜ�‹9ò Ø�‰Ð.Ó/ò 	DÜ	ˆS‹�Q‰Ò	B˜3˜q™6 S™=Ò	B¨S°©V°{Ð-Bðr   Úconfigc                 ó¶   — | j                  d«      dk7  ry| j                  d«      r| j                  d«      ryt        d„ | j                  dg «      D «       «      S )	zIReturn True when a Docker sandbox exposes host paths through bind mounts.Úenv_typeÚdockerFÚhost_cwdÚdocker_mount_cwd_to_workspaceTc              3   ó2   K  — | ]  }t        |«      –— Œ y ­w©N)rf   )Ú.0re   s     r   ú	<genexpr>z*_docker_has_host_access.<locals>.<genexpr>�   s   è ø€ Ò^°cÔ,¨S×1Ñ^ùs   ‚Údocker_volumes)ÚgetÚany)rg   s    r   Ú_docker_has_host_accessrt   Š   sO   € à‡z�z�*Ó Ò)ØØ‡z�z�*Ô &§*¡*Ð-LÔ"MØÜÑ^¸V¿Z¹ZÐHXÐZ\Ó=]Ô^Ó^Ð^r   FÚcommandri   Úhas_host_accessc                 ó0   — t        | |t        «       |¬«      S )zJDelegate to consolidated guard (tirith + dangerous cmd) with CLI callback.)Úapproval_callbackrv   )Ú_check_all_guards_implrE   )ru   ri   rv   s      r   Ú_check_all_guardsrz   “   s    € ô " '¨8Ü4JÓ4LØ2AôCð Cr   )ÚEnvironmentConnectionErroruù  Execute shell commands. The host OS, shell, and terminal backend are stated in your environment section â€” write commands for THAT platform. Filesystem, current working directory, and exported environment variables persist between calls.

Do NOT use cat/head/tail (use read_file), grep/rg/find/ls (use search_files), sed/awk (use patch), or echo/heredoc file creation (use write_file). Reserve terminal for: builds, installs, git, processes, scripts, network, package managers â€” anything that needs a shell. Output is auto-truncated with the full text saved to a file â€” never pipe through tail/head to shorten it.
Environment state persists: activate a virtualenv or export variables once per session, not before every command.

Foreground (default): returns INSTANTLY when the command finishes, even with a high timeout â€” set timeout generously for long builds and fixed waits.
Background: set background=true (returns a session_id) only for commands that must keep running independently after this tool call returns; add notify=true for bounded tasks, leave silent only for servers/daemons that never exit. Do not start sleep, timers, cooldowns, delays, or polling loops with background=true â€” to wait a fixed time, run the wait as a normal foreground command with a high enough timeout. After starting a server, verify readiness with a health check in a separate call (no blind sleep loops); manage with process(action="poll"/"wait").
Working directory: use 'workdir' for per-command cwd; when a command changes the session cwd (cd, pushd), trust the result's "cwd" field instead of prefixing every command with 'cd'.
PTY: pty=true + background=true for interactive CLIs (they hang without a terminal); drive them with process(action="write"/"submit"). Local backend only.
Ú_active_environmentsÚ_last_activityÚ_creation_locksÚcontainer_configc                 óð  — | j                  dd«      syt        ryt        5  t        r
	 ddd«       ydaddd«       	 t        t	        dd«      «      }t        d|«      dz  }	 d	d
lm	}m
} t        d«      5   || j                  dd«      «      } |||¬«      }|rt        j                  d||«       ddd«       y# 1 sw Y   Œ‡xY w# t
        t        f$ r d}Y Œ‡w xY w# t        $ r Y yw xY w# 1 sw Y   yxY w)u1  Run the docker orphan reaper once per process, if enabled.

    Sweeps Exited containers labeled ``hermes-agent=1`` for the current
    profile â€” leftovers of Hermes processes that died without firing
    ``atexit`` (SIGKILL, OOM, closed terminal). Conservative: only containers
    older than ``2 Ã— lifetime_seconds``, profile-scoped. Gates:
    ``terminal.docker_orphan_reaper: false`` (operator opt-out, e.g. several
    Hermes processes sharing a profile) and the once-per-interpreter flag so
    parallel subagent / RL-rollout calls don't re-sweep.
    Údocker_orphan_reaperTNÚTERMINAL_LIFETIME_SECONDSÚ300i,  é<   r^   r   )Úreap_orphan_containersÚ_container_identityzDocker orphan reaper raisedÚdocker_shared_container_keyr   )Úmax_age_secondsÚprofile_filterzADocker orphan reaper removed %d stale container(s) for profile %s)rr   Ú_docker_orphan_reaper_ranÚ_docker_orphan_reaper_lockÚintr"   r1   r2   ÚmaxÚtools.environments.dockerr…   r†   ÚImportErrorr    r3   Úinfo)r   ÚlifetimeÚmax_ager…   r†   ÚprofileÚremoveds          r   Ú_maybe_reap_docker_orphansr•   ¸   s  € ð ×ÑÐ 6¸Ô=ØÝ ØÜ	#ñ )Ý$Ø÷)ð )ð %)Ð!÷)ðÜ”uÐ8¸%Ó@ÓAˆô �"�hÓ !Ñ#€GðßYô 
Ð-Ó	.ñ Ù%Ð&6×&:Ñ&:Ð;XÐZ\Ó&]Ó^ˆÙ(¸ÐQXÔYˆÙÜ�K‰KØSØ˜ô÷	ð ÷')ð )ûô ”zÐ"ò ØŠðûô ò Ùðú÷ð ús?   ¡B:²B:½C Á"C Á5<C,Â:CÃCÃCÃ	C)Ã(C)Ã,C5Ú_task_env_overridesÚ_session_cwdÚ_container_aliasesÚsession_keyÚcwdc                 óÜ   — t        |t        «      r|j                  «       syt        | xs d«      }t        5  t        j                  |«      |k7  r	|t        |<   ddd«       y# 1 sw Y   yxY w)zÍRecord *cwd* as *session_key*'s working directory (after a completed
    command, or on workspace-override registration). None/empty keys collapse
    to ``"default"``; non-string / empty cwds are ignored.Nr,   )r`   r   ra   Ú_session_cwd_lockr—   rr   )r™   rš   Úkeys      r   Úrecord_session_cwdrž   û   s]   € ô �cœ3Ô s§y¡y¤{ØÜ
ˆkÒ&˜YÓ
'€CÜ	ñ $Ü×Ñ˜CÓ  CÒ'Ø #ŒL˜Ñ÷$÷ $ñ $ús   ·"A"Á"A+c                 ó~   — t         5  t        j                  t        | xs d«      «      cddd«       S # 1 sw Y   yxY w)zšRecorded cwd for *session_key*, or None. No fallback chain on purpose:
    callers decide what an absent record means. None/empty keys read ``"default"``.r,   N)rœ   r—   rr   r   ©r™   s    r   Úget_session_cwdr¡     s4   € ô 
ñ ?Ü×Ñ¤ KÒ$<°9Ó =Ó>÷?÷ ?ò ?ús   ‡"3³<c                 óf   — t         5  t        j                  | d«       ddd«       y# 1 sw Y   yxY w)z/Drop a session's cwd record (session teardown).N)rœ   r—   Úpopr    s    r   Úclear_session_cwdr¤     s*   € ä	ñ ,Ü×Ñ˜ dÔ+÷,÷ ,ñ ,ús   ‡'§0ÚenvÚnew_cwdc                 ó�  — t        | dd«      }|rt        |«      s|S t        |«      s|S t        | dd«      }t        |t        «      r€|r~t
        j                  j                  t
        j                  j                  |«      «      }t
        j                  j                  t
        j                  j                  |«      «      }||k(  ryy)u�  Cwd to write into a LIVE cached env, or None to leave it untouched.

    On container backends a raw host path (a desktop/TUI session registering its
    workspace, e.g. ``C:\Users\me`` or ``/Users/me/workspace``) cannot be the
    in-sandbox workdir: every file-tools ``_exec`` wrapper does
    ``builtin cd -- <env.cwd> || exit 126``, so a host cwd poisons all later
    file operations with an unrelated ``cd:`` error. The creation paths already
    sanitize this (``_is_unusable_container_cwd`` guards); the live-env write
    here is the one remaining unsanitized site. When the host path is the one
    mounted at ``/workspace`` (docker cwd passthrough), the session's directory
    is still reachable â€” remap instead of discarding, mirroring the env-creation
    remap in ``terminal_tool()``. Non-container backends apply the override
    verbatim (ACP project-root switching must keep working).
    ri   Nrk   ú
/workspace)	r?   r   r   r`   r   r/   ÚpathÚabspathÚ
expanduser)r¥   r¦   ri   Ú
host_mountÚ	candidateÚmounteds         r   Ú_sanitize_cwd_for_live_envr¯     s™   € ô �s˜J¨Ó-€HÙÔ0°Ô:ØˆÜ% gÔ.ØˆÜ˜˜j¨$Ó/€JÜ�*œcÔ"¡zÜ—G‘G—O‘O¤B§G¡G×$6Ñ$6°wÓ$?Ó@ˆ	Ü—'‘'—/‘/¤"§'¡'×"4Ñ"4°ZÓ"@ÓAˆØ˜ÒØØr   Útask_idÚ	overridesc                 ó†  — |t         | <   |j                  d«      }t        |t        «      r‹|j	                  «       rzt        | |«       t        | «      }t        5  t        j                  | «      xs t        j                  |«      }ddd«       �%t        |dd«      �t        ||«      }|�||_        yyyyyy# 1 sw Y   Œ3xY w)aq  Register per-task sandbox overrides (``docker_image``/``modal_image``/
    ``singularity_image``/``daytona_image``, ``env_type``, ``cwd``) before the
    agent loop runs.

    A ``cwd`` override takes effect immediately: it becomes the session's
    recorded cwd (until a ``cd`` changes it) and any live env's cwd is updated
    too, so env-side seeding stays consistent (ACP switching project root
    mid-session via ``session/load``). The session record keeps the RAW path
    (host workspaces are tracked there on purpose); only the live-env write is
    sanitized, since a host cwd can never be a container workdir.
    rš   N)r–   rr   r`   r   ra   rž   Ú_resolve_container_task_idÚ	_env_lockr|   r?   r¯   rš   )r°   r±   r¦   Úcontainer_idr¥   Ú	sanitizeds         r   Úregister_task_env_overridesr·   1  sÄ   € ð $-Ô˜Ñ à�m‰m˜EÓ"€GÜ�'œ3Ô G§M¡M¤OÜ˜7 GÔ,ô 2°'Ó:ˆÜñ 	^Ü&×*Ñ*¨7Ó3Ò]Ô7K×7OÑ7OÐP\Ó7]ˆC÷	^àˆ?œw s¨E°4Ó8ÐDÜ2°3¸Ó@ˆIØÐ$Ø#�•ð %ð  Eˆ?ð %4Ð÷	^ð 	^ús   Á-B7Â7C c                 ó¨   — t         j                  | d«       t        | «       t        5  t        j                  | d«       ddd«       y# 1 sw Y   yxY w)zJDrop a task's overrides, cwd record and container alias (rollout cleanup).N)r–   r£   r¤   Ú_container_alias_lockr˜   ©r°   s    r   Úclear_task_env_overridesr»   N  sB   € ä×Ñ˜G TÔ*Ü�gÔÜ	ñ .Ü×Ñ˜w¨Ô-÷.÷ .ñ .ús   ¨AÁAÚchild_task_idÚparent_task_idc                 ól   — | syt         5  t        |xs d«      t        | <   ddd«       y# 1 sw Y   yxY w)z�Make *child_task_id* resolve to *parent_task_id*'s container (called at
    delegate_task spawn). A missing parent id aliases to ``"default"``.Nr,   )r¹   r   r˜   )r¼   r½   s     r   Úregister_container_aliasr¿   V  s;   € ñ ØÜ	ñ MÜ,/°Ò0KÀ)Ó,LÔ˜=Ñ)÷M÷ Mñ Mús   Š*ª3c                 ó¼   — t        «       }| }t        5  |t        v r+||vr'|j                  |«       t        |   }|t        v r||vrŒ'ddd«       |S # 1 sw Y   |S xY w)uA   Follow the childâ†’parent alias chain (cycle-safe) for *task_id*.N)Úsetr¹   r˜   Úadd)r°   Úseenr�   s      r   Ú_resolve_container_aliasrÄ   _  sd   € ä‹5€DØ
€CÜ	ñ *ØÔ'Ñ'¨C°t©OØ�H‰H�SŒMÜ$ SÑ)ˆCð Ô'Ñ'¨C°tªO÷*ð €J÷	*ð €Jús   “3AÁA>   ri   Úmodal_imageÚdocker_imageÚdaytona_imageÚsingularity_imagec                 óx   — | r| t         vryt        t        t         |    j                  «       «      t        z  «      S )uÀ   True when *task_id* registered image/env_type overrides â€” the single
    "isolated RL/benchmark rollout" predicate shared by key resolution and
    container creation so the two can't drift.F)r–   rb   rÁ   ÚkeysÚ_ISOLATION_OVERRIDE_KEYSrº   s    r   Ú_has_isolation_overridesrÌ   p  s7   € ñ �gÔ%8Ñ8ØÜ”Ô'¨Ñ0×5Ñ5Ó7Ó8Ô;SÑSÓTÐTr   T)Úfrozenc                   óh   — e Zd ZU dZeed<   eed<   edefd„«       Zedefd„«       Z	edefd„«       Z
y)	Ú_SessionScopeuÓ  Backend identity + scoping predicates for one call, read once.

    ``env_type`` is the scope-aware TERMINAL_ENV; ``persistent`` is
    ``TERMINAL_CONTAINER_PERSISTENT``. Derived predicates:

    * ``session_isolated`` â€” non-persistent sandboxes get per-session identities:
      ``container_persistent: false`` means state must not survive or be shared
      across sessions, so one shared sandbox contradicts it. Docker, plus plugin
      backends declaring ``session_isolated_when_nonpersistent`` (sandboxes resumed
      by name, where a shared deterministic name would let two ephemeral runs
      attach one VM and delete it under each other).
    * ``docker_session_isolated`` â€” docker-only view: the workspace mount and
      session-scoped teardown paths must not fire for other backends.
    * ``docker_profile_scoped`` â€” docker + ``container_persistent: true``: ONE
      long-lived container per profile shared by every session (CLI, gateway,
      WebUI). The session-key fallback in :func:`_resolve_container_task_id` stops
      cross-profile SSH reuse; ungated it fragmented persistent Docker into one
      container per gateway session, so this restores profile scoping for exactly
      this backend/mode.
    ri   Ú
persistentr	   c                 óh   — | j                   dk7  rt        | j                   d«      sy| j                   S )Nrj   Ú#session_isolated_when_nonpersistentF)ri   r   rÐ   ©Úselfs    r   Úsession_isolatedz_SessionScope.session_isolated’  s2   € à�=‰=˜HÒ$Ô-=Ø�M‰MÐ@ô.
ð Ø—?‘?Ð"Ð"r   c                 ó<   — | j                   dk(  xr | j                  S ©Nrj   )ri   rÕ   rÓ   s    r   Údocker_session_isolatedz%_SessionScope.docker_session_isolatedš  s   € à�}‰} Ñ(ÒB¨T×-BÑ-BÐBr   c                 ó<   — | j                   dk(  xr | j                  S r×   ©ri   rÐ   rÓ   s    r   Údocker_profile_scopedz#_SessionScope.docker_profile_scopedž  s   € à�}‰} Ñ(Ò<¨T¯_©_Ð<r   N)Ú__name__Ú
__module__Ú__qualname__Ú__doc__r   Ú__annotations__rb   ÚpropertyrÕ   rØ   rÛ   rA   r   r   rÏ   rÏ   y  sf   … ñð( ƒMØÓàð# $ò #ó ð#ð ðC¨ò Có ðCð ð= tò =ó ñ=r   rÏ   c                  óX   — t        «        t        t        dd«      t        dd«      ¬«      S )uJ   Bridge config â†’ env once, then snapshot the backend scope for this call.ÚTERMINAL_ENVÚlocalÚTERMINAL_CONTAINER_PERSISTENTÚtruerÚ   )Ú_ensure_terminal_env_bridgedrÏ   r"   r#   rA   r   r   Ú_session_scoperè   £  s*   € ä Ô"ÜÜ�~ wÓ/ÜÐ=¸vÓFôð r   c                  ó*   — t        «       j                  S )zOSee :attr:`_SessionScope.docker_session_isolated` (used by the docker builder).)rè   rØ   rA   r   r   Ú!_docker_session_isolation_enabledrê   ¬  s   € äÓ×3Ñ3Ð3r   c                 ó4  — | rt        | «      r| S t        «       }| r|j                  rt        | «      S t	        «       }|j
                  rt        dd«      j                  «       nd}|rd|› �S |sy|j
                  sd|› �S t        «       xs d}|dk(  rdS d|› �S )uË  Map a tool-call ``task_id`` to the ``_active_environments`` key. Order matters â€”
    earlier branches are authoritative where they apply:

    1. Image/``env_type`` overrides (RL/benchmark rollouts) key their own sandbox;
       CWD-only overrides (ACP workspace tracking) are NOT isolation signals.
    2. Per-session isolation (docker + ``container_persistent: false``): each
       session's task_id is its own key (a fresh chat gets a fresh sandbox with only
       ITS mounts); delegate_task children follow the alias registry to the parent.
    3. Session key present (WebUI per-session, gateway per-message): persistent
       Docker is PROFILE-scoped â€” ``shared:<key>`` opt-in, else ``profile:<name>``,
       with the default profile staying literally ``"default"`` so CLI and
       default-profile gateway sessions share ONE container; other backends key
       ``session:<key>`` so switching profiles can't reuse another profile's
       SSHEnvironment on the wrong host.
    4. No session key (CLI): ``shared:<key>`` when opted in (else a CLI run of a
       keyed profile would split from its gateway sessions), else ``"default"``,
       which subagent ids collapse onto to share the parent's container.
    Ú$TERMINAL_DOCKER_SHARED_CONTAINER_KEYr   zshared:r,   zsession:zprofile:)	rÌ   rè   rÕ   rÄ   rR   rÛ   r"   ra   rU   )r°   Úscoper™   Úsharedr“   s        r   r³   r³   ±  s±   € ñ& Ô+¨GÔ4ØˆÜÓ€EÙ�5×)Ò)Ü'¨Ó0Ð0ô 'Ó(€KØJO×JeÒJeŒUÐ9¸2Ó>×DÑDÔFÐkm€FÙð ˜˜Ð!Ð!ÙØØ×&Ò&Ø˜+˜Ð'Ð'Ü&Ó(Ò5¨I€GØ 9Ò,ˆ9ÐF°H¸W¸IÐ2FÐFr   c                 ó€   — | xs d}t         j                  |«      xs" t         j                  t        |«      «      xs i S )a  Return the env overrides for *task_id*, raw key first then collapsed.

    ``register_task_env_overrides`` writes under the *raw* task/session id, but
    a CWD-only override collapses (:func:`_resolve_container_task_id`) to the
    shared ``"default"`` container. Callers must therefore read the raw id
    FIRST and only fall back to the collapsed container id, or the originating
    session's override is silently dropped. Single source of that lookup so
    the terminal and file layers can't drift apart.
    r,   )r–   rr   r³   )r°   r6   s     r   Úresolve_task_overridesrð   à  sB   € ð Ò
�Y€Cä×Ñ Ó$ò 	Ü×"Ñ"Ô#=¸cÓ#BÓCò	àðr   rÆ   rÈ   rÅ   rÇ   )rj   ÚsingularityÚmodalÚdaytonac                 ób   — t         j                  | «      }|€y|j                  |«      xs ||   S )zVImage for *env_type*: per-task override first, then config; "" for imageless backends.r   )Ú_IMAGE_KEY_BY_BACKENDrr   )ri   r±   rg   r�   s       r   Ú_select_imagerö   û  s2   € ä
×
#Ñ
# HÓ
-€CØ
€{ØØ�=‰=˜ÓÒ, ¨¡Ð,r   Úeffective_task_idc                 óx   — | |fD ]3  }|sŒ|t         v sŒt        j                  «       t        |<   t         |   c S  y)aŠ  Return the cached env for the collapsed id, else for the raw task_id, else None.

    Caller holds ``_env_lock``. Per-session surfaces (ACP/gateway/dashboard)
    with a CWD-only override collapse to ``"default"`` for container sharing,
    yet an env may already be cached under the originating task_id; honor it
    instead of spawning a duplicate. Refreshes ``_last_activity`` on a hit.
    N)r|   Útimer}   )r÷   r°   r�   s      r   Ú_lookup_active_envrú     sC   € ð " 7Ð+ò -ˆÚ�3Ô.Ò.Ü"&§)¡)£+ŒN˜3ÑÜ'¨Ñ,Ò,ð-ð r   c                 ó   — | j                  d«      dk7  s| j                  d«      syt        «       rt        |«      dk(  r| j                  d«      S t        |«      }|j                  d«      }|j                  d«      d	k(  s t	        |t
        «      r|j                  «       syt        j                  j                  t        j                  j                  |«      «      }t        j                  j                  |«      r|j                  d
«      ry|S )u«  Host directory to bind-mount at ``/workspace`` for *task_id*'s container.

    Single owner of the cwd-mount policy for every creation site. Shared-
    container mode: the ``TERMINAL_CWD``-derived ``config["host_cwd"]``.
    Per-session isolation (docker + ``container_persistent: false``): only
    the SESSION's own registered workspace may mount â€” the process env var is
    a launch artifact that outlives the session that set it, so deriving a
    fresh session's mount from it would leak the previous session's directory.
    Overrides tagged ``cwd_source: "process"`` are refused for the same reason;
    ``cwd_source: "session"`` or untagged (ACP/RL) overrides mount.
    ri   rj   rl   Nr,   rk   rš   Ú
cwd_sourceÚprocess©r¨   ú/root)rr   rê   r³   rð   r`   r   ra   r/   r©   rª   r«   Úisdirrc   )rg   r°   r±   r­   s       r   Ú_resolve_task_host_cwdr    sÐ   € ð ‡z�z�*Ó Ò)°·±Ð<[Ô1\Øä,Ô.Ô2LÈWÓ2UÐYbÒ2bØ�z‰z˜*Ó%Ð%Ü& wÓ/€IØ—‘˜eÓ$€IØ‡}�}�\Ó" iÒ/´zÀ)ÌSÔ7QÐYb×YhÑYhÔYjØÜ—‘—‘¤§¡× 2Ñ 2°9Ó =Ó>€Iä�7‰7�=‰=˜Ô# y×';Ñ';Ð<SÔ'TØØÐr   c                  ó8  — ddl m}   | «       �yddlm}  |«       �yt        rydat        d«      5  ddlm}m}  |«       }t        |j                  d«      t        «      r |dd¬	«       nd
t        j                  vr
 |dd¬	«       ddd«       y# 1 sw Y   yxY w)uó  Backfill TERMINAL_* env vars from config.yaml when no launcher did.

    CLI, gateway and TUI/dashboard PTY launches bridge ``terminal.*`` into env vars
    at startup; processes that skip those paths (``hermes serve``, Desktop
    in-process agents, desktop cron ticker, ACP) would otherwise fall back to the
    local backend even when config selects docker â€” running on the host the user
    meant to sandbox. Explicit keys in the ``terminal`` section override matching
    env values (possibly stale from ``hermes setup``); env values for omitted keys
    are preserved. Without a terminal section an existing TERMINAL_ENV is kept and
    defaults are backfilled only when none is set. A per-turn terminal scope
    suppresses the bridge entirely: writing scope values into the process-global
    env would re-create the first-writer-wins cross-profile leak the scope fixes.

    Ambient ``os.environ`` is the *launch* profile's authority only. Under a
    context-local ``HERMES_HOME`` override (multiplexed dashboard / gateway
    secondary profile), this bridge is a no-op â€” otherwise the first unscoped
    call under that override would latch the secondary profile's ``terminal.*``
    into process-global env and poison later unscoped launch-profile turns
    (#107422 residual of #68559). Routed profiles must bind a terminal scope
    instead (same rule as ``env_loader._reapply_terminal_config_bridge``).

    terminal_tool reads ALL terminal settings from os.environ (TERMINAL_*). See #61115, #65696.
    r   )Úget_terminal_scopeN)Úget_hermes_home_overrideTu.   terminal config â†’ env fallback bridge failed)Úapply_terminal_config_to_envÚread_raw_configÚterminal)r¥   Úoverriderã   F)Útools.terminal_scoper  Úhermes_constantsr  Ú!_terminal_config_bridge_attemptedr    Úhermes_cli.configr  r  r`   rr   Údictr/   Úenviron)r  r  r  r  Ú
raw_configs        r   rç   rç   3  sŽ   € õ0 8áÓÐ'Øå9áÓ!Ð-Øå(ØØ(,Ð%ä	Ð@Ó	Añ CßSá$Ó&ˆ
Ü�j—n‘n ZÓ0´$Ô7Ù(¨T¸DÖAØ¤2§:¡:Ñ-Ù(¨T¸EÕB÷C÷ Cñ Cús   ±ABÂBrZ   )ÚsshÚvercel_sandboxÚmount_docker_cwdc                 ó²  — | dk(  r
t        «       nt        j                  | d«      }t        d|«      }ddlm} |r( || |«      st        j                  j                  |«      }d}| dk(  rµ|r³t        j                  j                  t        j                  j                  t        d«      xs
 t        «       «      «      }t        |«      sOt        j                  j                  |«      r4t        j                  j                  |«      r|j                  d«      s|}d	}||fS t        | «      r,|r*t        |«      r||k7  rt         j#                  d
|| |«       |}||fS )a  ``(cwd, host_cwd)`` from TERMINAL_CWD for *env_type*.

    Container backends are sanity-checked: with Docker cwd passthrough the host
    path is remapped to /workspace and tracked as host_cwd; otherwise host paths
    are discarded in favor of the backend default.
    rä   rÿ   ÚTERMINAL_CWDr   )Ú_is_ssh_remote_tilde_cwdNrj   rþ   r¨   zeIgnoring TERMINAL_CWD=%r for %s backend (host/relative path won't work in sandbox). Using %r instead.)r!   Ú_DEFAULT_CWD_BY_BACKENDrr   r"   r  r  r/   r©   r«   rª   r   Úisabsr   rc   r   r   r3   r�   )ri   r  Údefault_cwdrš   r  rk   r­   s          r   Ú_resolve_config_cwdr  g  s&  € ð %-°Ò$7”,”.Ô=T×=XÑ=XÐYaÐcjÓ=k€KÜ
� Ó
,€CÝ:Ù
Ñ+¨H°cÔ:Ü�g‰g× Ñ  Ó%ˆØ€HØ�8ÒÑ 0Ü—G‘G—O‘O¤B§G¡G×$6Ñ$6´u¸^Ó7LÒ7^ÔP\ÓP^Ó$_Ó`ˆ	ä˜Ô#Ü—‘—‘˜iÔ(¬R¯W©W¯]©]¸9Ô-EÈi×NbÑNbÐczÔN{à ˆHØˆCð �ˆ=Ðô 
˜xÔ	(©SÔ5OÐPSÔ5TÐY\Ð`kÒYkÜ�‰ð Tà˜ ;ô	0ð ˆØ�ˆ=Ðr   c            
      óÔ  — d} t        «        t        dd«      }t        dd«      }t        |«      r+t	        ddt
        d«      }t	        d	d
«      }t	        dd«      }nd\  }}}|dk(  r}t	        ddt        j                  d«      }t	        ddt        j                  d«      }t	        ddt        j                  d«      }t	        ddt        j                  d«      }	t        dd«      }
ng g i g df\  }}}}	}
t        ||«      \  }}i d|“dt        t        dd«      «      “dt        d| «      “d|“dt        d d!| › �«      “d"t        d#| «      “d$t        d%| «      “d&t        d'd(«      j                  «       “d)|“d*|“d+|“d,t	        d-d.«      “d/t	        d0d1«      “d2t        d3d(«      “d4t        d5d(«      “d6t	        d7d8«      “d9t        d:d(«      “i d;t        d<t        d=d>«      «      “d?t        d@d«      “dA|“dB|“dC|“dDt        dEd>«      “dF|“dG|“dHt        dId«      “dJt        dKd«      “dLt        dMd>«      “dN|	“dO|
“dPt        dQd>«      “dRt        dSd(«      j                  «       “dTt        dUd>«      “¥S )VzAResolve the terminal configuration dict from TERMINAL_* env vars.z*nikolaik/python-nodejs:python3.11-nodejs20rã   rä   Ú&TERMINAL_DOCKER_MOUNT_CWD_TO_WORKSPACEÚfalseÚTERMINAL_CONTAINER_CPUÚ1r;   ÚTERMINAL_CONTAINER_MEMORYÚ5120ÚTERMINAL_CONTAINER_DISKÚ51200)g      ð?i   i È  rj   ÚTERMINAL_DOCKER_FORWARD_ENVz[]z
valid JSONÚTERMINAL_DOCKER_VOLUMESÚTERMINAL_DOCKER_ENVz{}ÚTERMINAL_DOCKER_EXTRA_ARGSÚTERMINAL_DOCKER_SHM_SIZEÚ1gri   Ú
modal_modeÚTERMINAL_MODAL_MODEÚautorÆ   ÚTERMINAL_DOCKER_IMAGEÚdocker_forward_envrÈ   ÚTERMINAL_SINGULARITY_IMAGEz	docker://rÅ   ÚTERMINAL_MODAL_IMAGErÇ   ÚTERMINAL_DAYTONA_IMAGEÚvercel_runtimeÚTERMINAL_VERCEL_RUNTIMEr   rš   rk   rl   ÚtimeoutÚTERMINAL_TIMEOUTÚ180Úlifetime_secondsr‚   rƒ   Ússh_hostÚTERMINAL_SSH_HOSTÚssh_userÚTERMINAL_SSH_USERÚssh_portÚTERMINAL_SSH_PORTÚ22Ússh_keyÚTERMINAL_SSH_KEYÚssh_persistentÚTERMINAL_SSH_PERSISTENTÚTERMINAL_PERSISTENT_SHELLræ   Úlocal_persistentÚTERMINAL_LOCAL_PERSISTENTÚcontainer_cpuÚcontainer_memoryÚcontainer_diskÚcontainer_persistentrå   rq   Ú
docker_envÚdocker_run_as_host_userÚ TERMINAL_DOCKER_RUN_AS_HOST_USERÚdocker_snap_compatÚTERMINAL_DOCKER_SNAP_COMPATÚdocker_networkÚTERMINAL_DOCKER_NETWORKÚdocker_extra_argsÚdocker_shm_sizeÚdocker_persist_across_processesÚ(TERMINAL_DOCKER_PERSIST_ACROSS_PROCESSESr‡   rì   r�   ÚTERMINAL_DOCKER_ORPHAN_REAPER)rç   r"   r#   r   r   ÚfloatÚjsonÚloadsr  r)   ra   )Údefault_imageri   r  rE  rF  rG  r-  rq   rI  rP  rQ  rš   rk   s                r   Ú_get_env_configrY  „  s‡  € à@€MÜ Ô"Ü�^ WÓ-€HÜ!Ð"JÈGÓTÐô
 ˜XÔ&Ü&Ð'?ÀÄeÈXÓVˆÜ)Ð*EÀvÓNÐÜ'Ð(AÀ7ÓK‰à:JÑ7ˆÐ'¨à�8ÒÜ+Ð,IÈ4ÔQU×Q[ÑQ[Ð]iÓjÐÜ'Ð(AÀ4ÌÏÉÐUaÓbˆÜ#Ð$9¸4ÄÇÁÈ\ÓZˆ
Ü*Ð+GÈÌtÏzÉzÐ[gÓhÐÜÐ :¸DÓA‰à]_ÐacÐegÐikÐmqÐ]qÑZÐ˜N¨JÐ8IÈ?ä'¨Ð2BÓC�M€Cˆð*Ø�Hð*àÔ'¬Ð.CÀVÓ(LÓMð*ð 	œÐ5°}ÓEð*ð 	Ð0ð	*ð
 	œUÐ#?À9È]ÈOÐA\Ó]ð*ð 	”uÐ3°]ÓCð*ð 	œÐ7¸ÓGð*ð 	œ%Ð 9¸2Ó>×DÑDÓFð*ð 	ˆsð*ð 	�Hð*ð 	(Ð)9ð*ð 	”>Ð"4°eÓ<ð*ð 	œNÐ+FÈÓNð*ð 	”EÐ-¨rÓ2ð*ð  	”EÐ-¨rÓ2ð!*ð" 	”NÐ#6¸Ó=ð#*ð$ 	”5Ð+¨RÓ0ñ%*ð* 	œ*Ø%¤uÐ-HÈ&Ó'Qó
ð+*ð0 	œJÐ'BÀGÓLð1*ð4 	˜ð5*ð6 	Ð,ð7*ð8 	˜.ð9*ð: 	¤
Ð+JÈFÓ Sð;*ð< 	˜.ð=*ð> 	�jð?*ð@ 	"¤:Ð.PÐRYÓ#ZðA*ðB 	œjÐ)FÈÓPðC*ðD 	œ*Ð%>ÀÓGðE*ðF 	Ð.ðG*ðH 	˜?ðI*ðN 	*¬:Ð6`ÐbhÓ+iðO*ðP 	&¤uÐ-SÐUWÓ'X×'^Ñ'^Ó'`ðQ*ðR 	¤
Ð+JÈFÓ SñS*ð *r   c                  ó  — t         rot        dt        j                  ¬«      5  t	        t        «       d   «       ddd«       t        d«      D ]  } t         s nt        j                  d«       Œ! t         rŒnyy# 1 sw Y   Œ?xY w)zKBackground thread worker that periodically cleans up inactive environments.zError in cleanup thread)Úlevelr6  Nr„   r\   )	Ú_cleanup_runningr    ÚloggingÚWARNINGr   rY  Úrangerù   Úsleep)Ú_s    r   Ú_cleanup_thread_workerrb  Í  sj   € å
ÜÐ-´W·_±_ÔEñ 	JÜ"¤?Ó#4Ð5GÑ#HÔI÷	Jä�r“ò 	ˆAÝ#ÙÜ�J‰J�q�Mð	÷ Ð
÷	Jð 	Jús   ¢A7Á7B c                  óÐ   — t         5  t        �t        j                  «       s1dat	        j
                  t        d¬«      at        j                  «        ddd«       y# 1 sw Y   yxY w)z;Start the background cleanup thread if not already running.NT)ÚtargetÚdaemon)r´   Ú_cleanup_threadÚis_aliver\  Ú	threadingÚThreadrb  ÚstartrA   r   r   Ú_start_cleanup_threadrk  Ø  sS   € ô 
ñ $ÜÐ"¬/×*BÑ*BÔ*DØ#ÐÜ'×.Ñ.Ô6LÐUYÔZˆOÜ×!Ñ!Ô#÷	$÷ $ñ $ús   ‡AAÁA%c                  ón   — da t        �	 t        j                  d¬«       yy# t        t        f$ r Y yw xY w)z#Stop the background cleanup thread.FNé   ©r3  )r\  rf  ÚjoinÚ
SystemExitÚKeyboardInterruptrA   r   r   Ú_stop_cleanup_threadrr  ã  sE   € ð ÐÜÐ"ð	Ü× Ñ ¨Ð Õ+ð #øô Ô-Ð.ò 	Ùð	ús   Š" ¢4³4c                  óø  — t        «        t        r~t        j                  dt	        t        «      «       t        t        j                  «       «      } t        «        | D ]/  }t        |dd«      }|€Œt        d«      5   |d¬«       ddd«       Œ1 dt        j                  v rBt        d«      5  t        j                  d   j                  j                  d¬«       ddd«       yy# 1 sw Y   Œ�xY w# 1 sw Y   yxY w)	zFStop the cleanup thread and shut down all remaining sandboxes on exit.z)Shutting down %d remaining sandbox(es)...Úwait_for_cleanupNzwait_for_cleanup raised on exitg      .@rn  ztools.environments.dockerzteardown drain raised on exit)rr  r|   r3   r�   rd   ÚlistÚvaluesr   r?   r    ÚsysÚmodulesÚDockerEnvironmentÚwait_for_all_teardowns)Úenvs_to_waitr¥   Úwait_fns      r   Ú_atexit_cleanupr}  î  sè   € äÔÝÜ�‰Ð?ÄÔEYÓAZÔ[ô
 Ô0×7Ñ7Ó9Ó:ˆÜ Ô"Øò 	*ˆCÜ˜cÐ#5°tÓ<ˆGØÑ"ÜÐ=Ó>ñ *Ù DÕ)÷*ð *ð	*ð #¤c§k¡kÑ1ÜÐ3Ó4ñ 	lÜ�K‰KÐ3Ñ4×FÑF×]Ñ]ÐfjÐ]Ôk÷	lð 	lð 2÷*ð *ú÷	lð 	lús   Á;
C$Â,.C0Ã$C-	Ã0C9c                 óŠ   — dj                  | j                  «       j                  «       «      }|j                  d«      xr d|v S )zÇTrue when PTY mode would break a stdin-driven command: `gh auth login
    --with-token` waits for EOF on piped stdin, and under a PTY
    `process.submit()` only sends a newline, so it hangs forever.Ú zgh auth loginz--with-token)ro  ÚlowerÚsplitrc   )ru   Ú
normalizeds     r   Ú_command_requires_pipe_stdinrƒ    s<   € ð —‘˜'Ÿ-™-›/×/Ñ/Ó1Ó2€JØ× Ñ  Ó1ÒR°nÈ
Ð6RÐRr   )Ú_foreground_background_guidanceÚ_safe_command_previewÚ_validate_workdirÚgateway_lifecycle_blockÚself_repo_block)Ú_YIELDED_NOTEÚspawn_background_processÚyield_to_background_handler)Úfinalize_foreground_resultÚnotify_on_completeÚ
backgroundc                 ó   — |r| r|ry|dfS )u7  Resolve notify_on_complete + watch_patterns both set: drop watch_patterns
    (combined they produce duplicate async notifications â€” one per match plus
    one on exit â€” that can spam the user long after the process ends).
    Returns ``(watch_patterns_to_use, conflict_note)``; note is "" without conflict.)Nzuwatch_patterns ignored because notify_on_complete=True; these two flags produce duplicate notifications when combinedr   rA   )r�  Úwatch_patternsrŽ  s      r   Ú#_resolve_notification_flag_conflictr‘    s   € ñ
 Ñ(©^ð
ð ˜2ÐÐr   )r™   ri   Úworkdirr  c                 ó�   — | r| S t        |«      }|r0t        |«      r%t        |«      rt        j	                  d|||«       |S |xs |S )uB  cwd for a command: explicit ``workdir`` > the session's own cwd record >
    ``default_cwd``.

    The record is written after every completed command of THIS session, so
    it is the session's ``cd`` state with no shared-env ambiguity. On
    container backends a recorded HOST path (a desktop/TUI surface registering
    its workspace) is unusable in the sandbox â€” ``cd <host path>`` fails with
    exit 126 â€” so it is discarded in favor of ``default_cwd``.

    Same guard class as the env-creation sanitizers (#50636, #54447); this is the per-command sibling site.
    zmIgnoring recorded session cwd %r for %s backend (host/relative path won't work in sandbox). Using %r instead.)r¡   r   r   r3   r�   )r’  r  r™   ri   Úrecordeds        r   Ú_resolve_command_cwdr•  #  sV   € ñ$ ØˆÜ˜{Ó+€HÙÔ)¨(Ô3Ô8RÐS[Ô8\Ü�‰ðLà�h ô	
ð
 ÐØÒ"�{Ð"r   éÿÿÿÿ)Ú	exit_codeÚstatusÚerrorr—  r˜  c                ól   — d|| dœ}|�||d<   |j                  |«       t        j                  |d¬«      S )zWThe terminal error envelope: ``output``/``exit_code``/``error`` (+ ``status``, extras).r   )Úoutputr—  r™  r˜  F©Úensure_ascii)ÚupdaterV  Údumps)r™  r—  r˜  ÚextraÚbodys        r   Ú_error_jsonr¢  B  s:   € à&(°yÈ5ÑQ€DØÐØˆˆX‰Ø‡K�K�ÔÜ�:‰:�d¨Ô/Ð/r   Úec                 óº   — ddl }|j                  «       }t        j                  d|«       t	        j
                  ddt        d| › �«      t        |«      ddœd	¬
«      S )zàLog the traceback and return the redacted error+traceback envelope.

    Exception text can embed the failing command line (and any secrets inline
    in it), so both fields are force-redacted before reaching the model.
    r   Nzterminal_tool exception:
%sr   r–  zFailed to execute command: r™  )r›  r—  r™  Ú	tracebackr˜  Frœ  )r¥  Ú
format_excr3   r™  rV  rŸ  r   )r£  r¥  Útb_strs      r   Ú_fatal_error_jsonr¨  K  s`   € ó Ø×!Ñ!Ó#€FÜ
‡L�LÐ/°Ô8Ü�:‰:ØØÜ,Ð/JÈ1È#Ð-NÓOÜ0°Ó8Øñð ôð r   c                   ó(   ‡ — e Zd ZdZdefˆ fd„Zˆ xZS )Ú	_Rejectedz¾Carries a finished tool-result JSON out of the planning/guard helpers, so
    each early-return site is one ``raise`` instead of an isinstance-checked
    ``str | plan`` union at the caller.Úresult_jsonc                 ó2   •— t         ‰| �  |«       || _        y rn   )ÚsuperÚ__init__r«  )rÔ   r«  Ú	__class__s     €r   r®  z_Rejected.__init__b  s   ø€ Ü‰Ñ˜Ô%Ø&ˆÕr   )rÜ   rÝ   rÞ   rß   r   r®  Ú__classcell__)r¯  s   @r   rª  rª  ]  s   ø„ ñ+ð' C÷ 'ñ 'r   rª  c                   ó4   — e Zd ZU dZdZee   ed<   dZe	ed<   y)Ú_ApprovalVerdictag  Outcome of the pre-exec guard pass.

    ``note`` is the audit note attached to the result. ``approved_run`` is True
    when the user explicitly approved (or pre-confirmed via ``force``); it drives
    the clean-interrupt-slate clear before ``env.execute`` so an approved command
    can't be SIGINT-killed by a bit that landed during the approval-wait.
    NÚnoteFÚapproved_run)
rÜ   rÝ   rÞ   rß   r³  r   r   rà   r´  rb   rA   r   r   r²  r²  g  s!   … ñð €Dˆ(�3‰-ÓØ€L�$Ôr   r²  r   c                óÎ  — |rt        d¬«      S t        | |t        |«      ¬«      }|d   sÝ|j                  d«      dk(  rlt	        t        ddd|j                  d| «      |j                  d	d
«      |j                  dd«      |j                  dd«      |j                  dd«      ¬«      «      ‚|j                  d	d
«      }d|› d�}t	        t        |j                  d|«      fddi|j                  d«      rd|d   i¤Ž«      ‚i ¤Ž«      ‚|j                  d	d«      }|j                  d«      rt        d|› d�d¬«      S |j                  d«      rt        d|› d�¬«      S t        «       S )z«Run tirith + dangerous-command guards; ``force`` skips them entirely.
    Raises :class:`_Rejected` when the command may not run (denied, or pending
    gateway approval).T)r´  )rv   Úapprovedr˜  Úpending_approvalr   ru   Údescriptionzcommand flaggedÚpattern_keyÚsmart_deniedFÚallow_permanent)r˜  Úapproval_pendingru   r¸  r¹  rº  r»  zCommand denied: z?. Use the approval prompt to allow it, or rephrase the command.ÚmessageÚblockedÚuser_summaryzflagged as dangerousÚuser_approvedzCommand required approval (z) and was approved by the user.)r³  r´  Úsmart_approvedzCommand was flagged (z&) and auto-approved by smart approval.)r³  )r²  rz   rt   rr   rª  r¢  )ru   ri   rg   r   rD   ÚdescÚfallback_msgs          r   Ú_run_approval_guardsrÄ  t  sž  € ñ Ü¨TÔ2Ð2Ü  ¨(ÔD[Ð\bÓDcÔd€HØ�JÒØ�<‰<˜Ó!Ð%7Ò7ÜœKØÐ-Ø!%Ø Ÿ™ Y°Ó8Ø$ŸL™L¨Ð8IÓJØ$ŸL™L¨¸Ó;Ø%Ÿ\™\¨.¸%Ó@Ø (§¡Ð->ÀÓ Eôó ð ð �|‰|˜MÐ+<Ó=ˆà˜t˜fð %Lð Lð 	ô œ H§L¡L°¸LÓ$Iñ |ÐR[ð |ØU]×UaÑUaÐbpÔUq¨¸ÀÑ8PÐ'Qñ|ó }ð 	}Øwyñ|ó }ð 	}à�<‰<˜Ð'=Ó>€DØ‡|�|�OÔ$ÜØ.¨t¨fÐ4SÐTØô
ð 	
ð ‡|�|Ð$Ô%ÜÐ'<¸T¸FÐBhÐ%iÔjÐjÜÓÐr   c                   ó|   — e Zd ZU dZeeef   ed<   eed<   eed<   eed<   eed<   ee   ed<   e	ed<   d	Z
ee	   ed
<   y	)Ú	_ExecPlanzIPer-call execution parameters resolved before any environment is touched.rg   ri   r÷   Úimagerš   rk   Úeffective_timeoutNÚ promoted_from_foreground_timeout)rÜ   rÝ   rÞ   rß   r   r   r   rà   r   rŒ   rÉ  rA   r   r   rÆ  rÆ  ˜  sI   … áSØ��c�‰NÓØƒMØÓØƒJØ	ƒHØ�s‰mÓØÓð 7;Ð$ h¨s¡mÔ:r   rÆ  aR  Requested foreground timeout {requested}s exceeds the {cap}s cap, so this command was started as a tracked background process with notify_on_complete=true instead of being refused. Do NOT re-run it. Its completion (exit code + output tail) arrives as a notification; poll with process(action="poll", session_id=...) if you need it sooner.r3  Ú_host_localc          
      ó  — t        | t        «      sUt        j                  dt	        | «      j
                  «       t        t        dt	        | «      j
                  › �d¬«      «      ‚t        «       }|rdn|d   }|sddl	m
}  |«        t        |«      }|rd	|› �}t        |«      }	t        ||	|«      }
|	j                  d
«      xs t        |«      xs |d
   }t!        ||«      }t#        |«      r3t%        |«      r(|rdn|d
   }||k7  rt        j'                  d|||«       |}|�|dk  rt        t)        d|› d�«      «      ‚d}|s0t+        | «      }|rt        t        |d¬«      «      ‚|r|t,        kD  r|}t/        ||||
|||xs |d   |¬«      S )a  Resolve backend, env-cache key, image, cwd and timeout for one call.

    Raises :class:`_Rejected` when the call is rejected up front (non-string
    command, non-positive or over-cap timeout, a foreground command that must
    run in the background).
    z+Rejected invalid terminal command value: %sz&Invalid command: expected string, got r™  ©r˜  rä   ri   r   )Úenforce_no_refusalzhost-local-rš   r¨   zbRemapping host/relative cwd override %r for %s backend (won't exist in sandbox). Using %r instead.Nz2timeout must be a positive number of seconds (got z).r3  )rg   ri   r÷   rÇ  rš   rk   rÈ  rÉ  )r`   r   r3   r4   ÚtyperÜ   rª  r¢  rY  r	  rÍ  r³   rð   rö   rr   r¡   r  r   r   r�   r   r„  ÚFOREGROUND_MAX_TIMEOUTrÆ  )ru   r°   r3  rŽ  rÊ  rg   ri   rÍ  r÷   r±   rÇ  rš   rk   ÚremappedÚpromotedÚguidances                   r   Ú_plan_executionrÓ  ¯  s´  € ô �gœsÔ#Ü�‰ÐDÄdÈ7Ãm×F\ÑF\Ô]ÜœØ4´T¸'³]×5KÑ5KÐ4LÐMÐV]ô
ó ð 	ô Ó€FÙ%‰w¨6°*Ñ+=€Hñ Ý;áÔä2°7Ó;ÐÙð *Ð*;Ð)<Ð=Ðô
 ' wÓ/€IÜ˜( I¨vÓ6€Eà
�-‰-˜Ó
Ò
K¤/°'Ó":Ò
K¸fÀU¹m€CÜ% f¨gÓ6€Hô ˜XÔ&Ô+EÀcÔ+JÙ#+‘<°¸±ˆØ�(Š?Ü�K‰Kð>à�X˜xôð
 ˆð Ð˜w¨!š|Üœ
Ð%WÐX_ÐW`Ð`bÐ#cÓdÓeÐeØ€HÙô 3°7Ó;ˆÙÜœK¨¸ÔAÓBÐBÙ�wÔ!7Ò7ØˆHäØ Ð<MØ˜ xÀ7ÒC_ÈfÐU^ÑN_Ø)1ôð r   a&  Requested foreground timeout {requested}s exceeds the {cap}s cap, so this command was started as a tracked background process instead of being refused. Do NOT re-run it. This session cannot receive completion notifications, so poll it with process(action="poll", session_id=...) until it exits.r«  Úrequested_timeoutc                 ó>  — 	 t        j                  | «      }t	        |t
        «      r|j                  d«      r| S |j                  d«      rt        nt        }|j                  |t        ¬«      |d<   t        j                  |d¬«      S # t        t        f$ r | cY S w xY w)a(  Attach the foreground->background promotion note to a spawn result (unchanged on error). The
    note only promises a notification when the spawn actually kept notify_on_complete (finite sessions
    such as one-shot runners cannot route one back; the spawn already said so and cleared the flag).r™  r�  )Ú	requestedÚcapÚpromoted_from_foregroundFrœ  )rV  rW  r1   r2   r`   r  rr   Ú_PROMOTED_NOTEÚ_PROMOTED_NOTE_POLL_ONLYÚformatrÏ  rŸ  )r«  rÔ  ÚdataÚtemplates       r   Ú_with_promoted_noterÞ  	  s�   € ðÜ�z‰z˜+Ó&ˆô �dœDÔ! T§X¡X¨gÔ%6ØÐØ!%§¡Ð*>Ô!?�~ÔE]€HØ'/§¡ÐARÔXn Ó'o€DÐ	#Ñ$Ü�:‰:�d¨Ô/Ð/øô ”zÐ"ò ØÒðús   ‚B ÂBÂBÚplanc                 ó  — t        «        | j                  | j                  }}t        5  t	        ||«      }ddd«       �|S t
        5  t        j                  |t        j                  «       «      }ddd«       5  t        5  t	        ||«      }ddd«       |�|cddd«       S |dk(  r
t        «        t        j                  d||dd «       	 t        | j                  || j                  | j                   | j"                  || j$                  |dk(  rd| j                  j'                  dd«      ind¬	«      }t        5  |t0        |<   t3        j2                  «       t4        |<   ddd«       t        j                  d||dd «       |cddd«       S # 1 sw Y   �ŒZxY w# 1 sw Y   �Œ,xY w# 1 sw Y   �ŒxY w# t(        $ r(}t+        t-        t/        d
|› d�«      d¬«      «      ‚d}~ww xY w# 1 sw Y   Œ‰xY w# 1 sw Y   yxY w)aJ  Cached env for the task, else create it under the per-task creation lock.

    Concurrent calls for the same task_id wait for the first sandbox instead
    of each creating their own; the cache is re-checked under that lock.
    Raises :class:`_Rejected` with the ``"disabled"`` envelope when creation
    raises ImportError.
    Nrñ   z*Creating new %s environment for task %s...é   rä   rÐ   rC  F)rÇ  rš   r3  r°   rk   Úlocal_configz5Terminal tool disabled: environment creation failed (ú)ÚdisabledrÌ  z %s environment ready for task %s)rk  ri   r÷   r´   rú   Ú_creation_locks_lockr~   Ú
setdefaultrh  ÚLockr   r3   r�   r   rg   rÇ  rš   rÈ  rk   rr   r�   rª  r¢  r   r|   rù   r}   )rß  r°   ri   Úeffr¥   Ú	task_lockÚnew_envr£  s           r   Ú_acquire_envrë    sè  € ô ÔØ—M‘M 4×#9Ñ#9ˆc€Hä	ñ 4Ü% c¨7Ó3ˆ÷4à
€Øˆ
ä	ñ FÜ#×.Ñ.¨s´I·N±NÓ4DÓEˆ	÷Fð 
ñ Üñ 	3Ü$ S¨'Ó2ˆC÷	3àˆ?Ø÷	ñ ð �}Ò$Ü%Ô'Ü�‰Ð@À(ÈCÐPRÐQRÈGÔTð	Ü,Ø—‘˜X¨T¯Z©Z¸T¿X¹XØ×.Ñ.¸ÀdÇmÁmð   7Ò*ð " 4§;¡;§?¡?Ð3EÀuÓ#MÑNØ04ôˆGô ñ 	.Ø(/Ô  Ñ%Ü"&§)¡)£+ŒN˜3Ñ÷	.ô 	�‰Ð6¸À#ÀbÀqÀ'ÔJØ÷9ñ ÷4ñ 4ú÷
Fñ Fú÷	3ñ 	3ûô" ò 	ÜœKÜ+Ð.cÐdeÐcfÐfgÐ,hÓiØ!ôó ð ûð	ú÷	.ð 	.ú÷1ð ús~   ©FÁ)FÁ;G5ÂF(ÂG5Â$)G5ÃA(F5Ä6G5Ä<%G)Å!#G5ÆFÆF%Æ(F2	Æ-G5Æ5	G&Æ>#G!Ç!G&Ç&G5Ç)G2	Ç.G5Ç5G>c                 ó,   — t        dd| i|¤Ž}|�d|iS i S )zI``env.execute`` kwargs enabling yield-to-background (local backend only).ru   Úyield_handlerrA   )r‹  )ru   ÚctxÚhandlers      r   Ú_yield_kwargsrð  J  s)   € ä)ÑA°'ÐA¸SÑA€GØ)0Ð)<ˆO˜WÐ%ÐDÀ"ÐDr   Ú
session_idÚapproval_noteÚclear_interruptc                ó  — d}	|j                   |j                  |j                  }}}
|rddlm}  |«        t        |	dz   «      D ]C  }	 t        ||j                  ||
¬«      } |j                  | f||ddœt        | |
||||¬«      ¤Ž} n j/                  d«      rFt1        j2                  |j/                  dd«      ddd|d   |j/                  d«      dt4        dœd¬«      S t7        | |||
||||||¬«      S # t        $ �r}d	t        |«      j                  «       v rt        d
|› d�d¬«      cY d}~c S ||	k  r_d|dz   z  }t        j                  d||dz   |	t!        | «      t#        |«      j$                  |||
«	       t'        j(                  |«       Y d}~�ŒTt        j+                  d|	t!        | «      t#        |«      j$                  |||
«       t        t-        dt#        |«      j$                  › d|› �«      «      cY d}~c S d}~ww xY w)zHExecute in the foreground with retry on transient errors, then finalize.r[   r   )Úclear_current_thread_interruptr\   )r’  r  r™   ri   T)r3  rš   Úbounded_capture)ri   rš   r÷   r°   r™   r3  zCommand timed out after z secondsé|   )r—  Nr^   zfExecution error, retrying in %ds (attempt %d/%d) - Command: %s - Error: %s: %s - Task: %s, Backend: %szWExecution failed after %d retries - Command: %s - Error: %s: %s - Task: %s, Backend: %szCommand execution failed: z: Úyielded_session_idr›  r   Úyielded_to_backgroundÚpid)r›  r—  r™  r˜  rñ  rú  r�  r³  Frœ  )ru   Úresultr¥   ri   r÷   r°   rñ  r™   r’  Úcommand_cwdrò  )ri   r÷   rÈ  Útools.interruptrõ  r_  r•  rš   Úexecuterð  Ú	Exceptionr   r€  r¢  r3   r4   r…  rÎ  rÜ   rù   r`  r™  r   rr   rV  rŸ  r‰  rŒ  )ru   r¥   rß  r°   rñ  r™   r’  rò  ró  Úmax_retriesri   rè  rÈ  rõ  Úretry_countrü  rû  r£  Ú	wait_times                      r   Ú_run_foregroundr  P  s>  € ð €KØ'+§}¡}°d×6LÑ6LÈd×NdÑNdÐ$ˆc€Hñ ÝBÙ&Ô(ä˜[¨1™_Ó-ò rˆð	rÜ.Ø¨T¯X©XÀ;ÐYaôˆKð !�S—[‘[ØðØ!2¸ÐUYñä °(ÀÐ_bØ(/¸[ôJñˆFñ
 ðrð8 ‡z�zÐ&Ô'Ü�z‰zØ—j‘j ¨2Ó.¸TÈDØ-¸VÐDXÑ=YØ—:‘:˜eÓ$¸DÌ-ñ
ð ô	ð 	ô
 &Ø ¨C¸(ÐVYØ J¸KÐQXØ¨}ôð øô) ó 	rØœC ›FŸL™L›NÑ*Ü"Ð%=Ð>OÐ=PÐPXÐ#YÐehÔi×ià˜[Ò(Ø +°¡/Ñ2�	Ü—‘ð   HØ(¨+¸©/¸;ÔH]Ð^eÓHfÔhlÐmnÓho×hxÑhxÐz{ð  ~Að  CKôLä—
‘
˜9Ô%ÝÜ�L‰LÐrØ$Ô&;¸GÓ&DÄdÈ1Ãg×FVÑFVÐXYÐ[^Ð`hôjäÔ:Ð=WÔX\Ð]^ÓX_×XhÑXhÐWiÐikÐlmÐknÐ;oÓpÓq×qûð	rús2   Á?C7Ã7
HÄ+HÄ,HÄ4AHÆA#HÇ;HÈHé   c                ó  — t        | |||||¬«      }|rt        |«      ‚|rFt        |«      }|r9t        j	                  d|dd t        | «      «       t        t        |d¬«      «      ‚|dk(  rt        | |||¬«      }|rt        |«      ‚yy)	zòRaise :class:`_Rejected` with the blocked-result JSON when the command must not run.

    Order matters: gateway lifecycle first (protects the running gateway),
    then the dangerous-workdir check, then the self-repo guard (local only).
    )ru   r¥   ri   rš   r’  r™   z+Blocked dangerous workdir: %s (command: %s)NéÈ   r¾  rÌ  rä   )ru   rš   r’  r™   )r‡  rª  r†  r3   r4   r…  r¢  rˆ  )ru   r¥   ri   rš   r’  r™   r¾  Úworkdir_errors           r   Ú_pre_exec_blockr  ‘  s    € ô &Ø˜S¨8¸ÀgÐ[fô€Gñ Ü˜Ó Ð ÙÜ)¨'Ó2ˆÙÜ�N‰NÐHØ" 4 C˜=Ô*?ÀÓ*HôJäœK¨¸iÔHÓIÐIØ�7ÒÜ!¨'°sÀGÐYdÔeˆÙÜ˜GÓ$Ð$ð ð r   zÉPTY disabled for this command because it expects piped stdin/EOF (for example gh auth login --with-token). For local background processes, call process(action='close') after writing so it receives EOF.c           	      ó‚  — t        dd«      j                  «       j                  «       dk(  rt        | «      S t        j                  d| j                  «       t        d«      5  t        |«       ddd«       t        j                  ddd	| j                  | j                  d
| j                  › �dœd¬«      S # 1 sw Y   ŒHxY w)a  Infrastructure failure (SSH host down, Docker daemon unreachable), distinct
    from a nonzero exit. ``terminal.degraded_mode``: warn (default) returns a
    structured degraded result with a retry hint; fail preserves the historical
    error+traceback result.ÚTERMINAL_DEGRADED_MODEÚwarnÚfailzterminal backend degraded: %szdegraded-env eviction failedNr   r–  ÚdegradedzTerminal backend degraded: )r›  r—  r˜  ÚreasonÚ
retry_hintr™  Frœ  )r"   ra   r€  r¨  r3   r4   r  r    r   rV  rŸ  r  )r£  r°   s     r   Ú_degraded_resultr  ³  sª   € ô
 Ð% vÓ.×4Ñ4Ó6×<Ñ<Ó>À&ÒHÜ  Ó#Ð#Ü
‡N�NÐ2°A·H±HÔ=ä	Ð.Ó	/ñ -Ü# GÔ,÷-ä�:‰:ØØØØ—(‘(Ø—l‘lØ.¨q¯x©x¨jÐ9ñð ôð ÷-ð -ús   Á"B5Â5B>Úptyr�  Ú_completion_output_charsÚ	heartbeatc                 óP  ‡ ‡‡‡‡‡— 	 t        ‰ ||||
¬«      }t        ||«      Š|j                  |j                  |j                  cŠŠ}ddlm}  |d¬«      xs |xs dŠddlm} ddl	m
} t        |j                  t        «      }|j                  t        j                   «       j"                  «      }	  |ˆ ˆˆˆˆˆfd„|d	¬
«      }|j%                  |«       |j&                  rt)        t+        d|› d�d¬«      «      ‚t-        ‰ ‰|j.                  |¬«      }|xr t1        ‰ «      }|j2                  �d\  }}}	|rUt5        ‰ ‰‰||‰‰‰|xr | ||	|j6                  |rt8        nd||¬«      }|j2                  �t;        ||j2                  «      }|S t=        ‰ ‰|||‰‰|j6                  |j>                  ¬«	      S # |j%                  |«       w xY w# t(        $ r}|j@                  cY d}~S d}~wtB        $ r}tE        ||«      cY d}~S d}~wtF        $ r}tI        |«      cY d}~S d}~ww xY w)uÈ  Execute *command* in the configured terminal environment; returns a JSON string.

    ``force`` (internal, not in the model schema) skips the dangerous-command
    check after the user confirmed. ``workdir`` is per-command and never
    recorded as the session cwd. ``pty`` applies to the local backend only.
    ``notify_on_complete`` and ``watch_patterns`` are mutually exclusive
    background-only flags: on conflict watch_patterns is dropped. watch_patterns
    is hard rate-limited (1 notification / 15s / process) and auto-disabled
    after repeated strikes or a lifetime cap, promoting to notify_on_complete â€”
    use it only for rare one-shot signals on long-lived processes. ``heartbeat`` (seconds,
    background-only, implies notify_on_complete) emits a "still running + output since last
    time" event every N seconds so the agent stays current on a long job without polling.
    ``_completion_output_chars`` (internal) sizes the completion notification's output for a
    spawner whose output is the payload (a bot DM's reply); 0 keeps the usual tail.
    ``_host_local`` forces the local backend for Hermes-owned control-plane
    children (kept in a separate env cache from the configured backend).
    )r°   r3  rŽ  rÊ  r   )Úget_current_session_keyr   )r,   )Úrun_bounded_sync)Úacting_for_tidc                  ó&   •— t        ‰ ‰‰‰‰‰¬«      S )N)r¥   ri   rš   r’  r™   )r  )ru   rš   r¥   ri   r™   r’  s   €€€€€€r   ú<lambda>zterminal_tool.<locals>.<lambda>  s   ø€ œØ ¨x¸SÈ'Ð_jô€ r   zterminal.pre-exec-guard)Úlabelz3Terminal pre-execution guard did not finish within zKs (process-identity probe wedged); the command was not run. Retry the call.r™  rÌ  r   N)TTN)ru   r¥   ri   r÷   r°   r™   r’  rš   Úeffective_ptyr�  r�  rò  Úpty_disabled_reasonÚcompletion_output_charsÚheartbeat_seconds)r°   rñ  r™   r’  rò  ró  )%rÓ  rë  ri   rš   r÷   Útools.approvalr  Úagent.deadliner  rý  r  r�   rÈ  Ú_PRE_EXEC_GUARD_MIN_TIMEOUT_SrÁ   rh  Úcurrent_threadÚidentÚresetÚ	timed_outrª  r¢  rÄ  rg   rƒ  rÉ  rŠ  r³  Ú_PTY_DISABLED_REASONrÞ  r  r´  r«  r{   r  rÿ  r¨  )ru   rŽ  r3  r°   rñ  r   r’  r  r�  r�  rÊ  r  r  rß  r÷   r  r  r  Úguard_timeoutÚ_acting_tokenÚbounded_guardÚverdictÚpty_disabledrû  Úrr£  rš   r¥   ri   r™   s   `     `                   @@@@r   Úterminal_toolr-  È  s6  ý€ ð@M$ÜØ˜W¨gÀ*ÐZeô
ˆô ˜4 Ó)ˆØ+/¯=©=¸$¿(¹(ÀD×DZÑDZÐ(ˆ�#Ð(õ
 	;á-°bÔ9ÒL¸gºmÈˆõ 	4Ý2ô ˜D×2Ñ2Ô4QÓRˆØ&×*Ñ*¬9×+CÑ+CÓ+E×+KÑ+KÓLˆð		0Ù,÷ð ð Ø/ôˆMð × Ñ  Ô/Ø×"Ò"ÜœKØEÀmÀ_ð U\ð \àôó ð ô ' w°¸$¿+¹+ÈUÔSˆàÒDÔ;¸GÓDˆØ×0Ñ0Ð<ð >NÑ:ˆJÐ*¨NÙÜ-Ø S°8ÐO`Ø¨[À'ÈsØ!Ò6¨,Ð&6ÐK]Ø-¸W¿\¹\Ù<HÕ$8ÈdØ(@Ø"+ôˆFð ×4Ñ4Ð@Ü,¨V°T×5ZÑ5ZÓ[�ØˆMÜØ�S˜$Ø¨
ÀØ¨7¯<©<È×I]ÑI]ô
ð 	
øð; × Ñ  Õ/ûôD ò Ø�}‰}ÕûÜ%ò ,Ü  7Ó+Õ+ûÜò $Ü  Ó#Õ#ûð$úsa   ˆB+G Â4F7 ÃCG Æ'G Æ7G
Ç
G Ç	H%ÇG'Ç!H%Ç'H%Ç3HÇ>H%ÈH%È
H ÈH%È H%c                  óÚ   — 	 t        «       } t        j                  | d   t        «      } || «      S # t        $ r0}t
        j                  d|d¬«       t        d|› �«       Y d}~yd}~ww xY w)z¬Check if all requirements for the terminal tool are met. The reason for a failure is kept for
    :func:`terminal_backend_unavailable_reason` (CLI startup notice / doctor).ri   z&Terminal requirements check failed: %sT)Úexc_infozthe requirements check failed: NF)rY  r$   rr   r&   rÿ  r3   r™  r'   )rg   Úcheckerr£  s      r   Úcheck_terminal_requirementsr1  8  sg   € ðÜ Ó"ˆÜ'×+Ñ+¨F°:Ñ,>Ô@ZÓ[ˆÙ�v‹ÐøÜò Ü�‰Ð=¸qÈ4ˆÔPÜ"Ð%DÀQÀCÐ#HÔIÜûðús   ‚.1 ±	A*º&A%Á%A*)Úregistryr  ÚobjectÚstringzThe shell command to execute)rÎ  r¸  Úbooleanu$  Run in the background, returning a session_id. Pair with notify=true for anything with a defined end (tests, builds, deploys) â€” without it the process runs silently. Only servers/watchers/daemons that never exit should stay silent. Short commands: prefer foreground with a generous timeout.)rÎ  r¸  r,   z3Max seconds to wait (default: 180, foreground max: u�   ). Returns INSTANTLY when command finishes â€” set high for long tasks, you won't wait unnecessarily. A foreground timeout above zs runs the command as a tracked background process with notify_on_complete=true instead (the result says so; do not re-run it).r\   )rÎ  r¸  Úminimumz^Working directory for this command (absolute path). Defaults to the session working directory.z�With background=true: run in a pseudo-terminal for interactive CLI tools (Codex, Claude Code, Python REPL). Local backend only. Default: false.u£  With background=true: notify=true fires exactly one notification when the process exits (the right choice for nearly every bounded task â€” builds, tests, deploys). notify=['pattern', ...] instead notifies when a line matches a pattern â€” ONLY for one-shot readiness signals on processes that never exit (e.g. ['Application startup complete']); rate-limited and auto-disabled if it over-fires. Omit for silent daemons.rÎ  Úarray)rÎ  Úitems)r¸  ÚanyOfr„   z»With background=true: also notify every N seconds (min 60) with the output since the last notice. For long jobs you must react to mid-run (merge trains, full suites); implies notify=true.)rÎ  r6  r¸  )ru   rŽ  r3  r’  r  Únotifyr  )rÎ  Ú
propertiesÚrequired)r+   r¸  Ú
parametersc                 ó0  — ddl m}  || «       d| vrd| v rt        d«      S | j                  d«      }| j                  dd«      }| j                  d	«      }| j                  d
«      xs d}t	        |t
        «      rt	        |t        «      s|dk  rt        d«      S | j                  dd«      s0|s|s|s|rt        d«      S | j                  dd«      rt        d«      S |�5t	        |t        «      r|}d }n t	        |t        «      r|}d}nt        d«      S |rd}t        | j                  d«      | j                  dd«      | j                  d«      |j                  d«      |j                  d«      | j                  d«      | j                  dd«      |||¬«
      S )Nr   )Úvalidate_prepared_terminalru   Úcodez¥terminal received a 'code' parameter, but it requires a shell command in 'command'. Use execute_code(code=...) for Python; for shell, retry as terminal(command=...).r:  r�  Fr�  r  z5heartbeat must be a whole number of seconds (min 60).rŽ  z¨notify/heartbeat only apply to background commands (foreground results return directly). Either drop them, or run as terminal(command=..., background=true, notify=...).r  zÊpty requires background=true (a PTY session is interacted with via process(action='write'/'submit'), which needs a tracked background process). Retry as terminal(command=..., background=true, pty=true).zanotify must be true/false (notify on exit) or a list of strings (notify on output pattern match).Tr3  r°   rñ  r’  )
ru   rŽ  r3  r°   rñ  r’  r  r�  r�  r  )	Úagent.terminal_approval_batchr?  r   rr   r`   rŒ   rb   ru  r-  )ÚargsÚkwr?  r:  r�  r�  r  s          r   Ú_handle_terminalrD  y  s£  € ÝHÙ˜tÔ$ð ˜Ñ ¨4¡Üð9ó
ð 	
ð �X‰X�hÓ€FØŸ™Ð"6¸Ó>ÐØ—X‘XÐ.Ó/€NØ—‘˜Ó%Ò*¨€IÜ�i¤Ô%¬°I¼tÔ)DÈ	ÐTUÊÜÐQÓRÐRØ�8‰8�L %Ô(Ù‘^Ñ'9¹YÜðFóð ð
 �8‰8�E˜5Ô!Üð.óð ð ÐÜ�fœdÔ#Ø!'ÐØ!‰NÜ˜¤Ô%Ø#ˆNØ!&Ñäð<óð ñ Ø!ÐÜØ—‘˜Ó#Ø—8‘8˜L¨%Ó0Ø—‘˜Ó#Ø—‘�yÓ!Ø—6‘6˜,Ó'Ø—‘˜Ó#Ø�H‰H�U˜EÓ"Ø-Ø%Øôð r   u   ðŸ’»i † )r+   ÚtoolsetÚschemarï  Úcheck_fnÚemojiÚmax_result_size_chars)ÚPath)útools.terminal_tool_lifecycleÚ
cleanup_vm)ÚutilsÚenv_var_enabled)rK  Úget_active_env)útools.tool_backend_helpersÚhas_direct_modal_credentials)ztools.interruptÚis_interrupted)ztools.managed_tool_gatewayÚis_managed_tool_gateway_ready)rK  Úis_persistent_env)rP  Ú%nous_tool_gateway_unavailable_message)rP  Úresolve_modal_backend_state)ztools.shell_heredocÚstrip_inert_heredoc_bodies)
rL  rN  rO  rQ  rR  rS  rT  rU  rV  rW  c                 óÎ   — t         j                  | «      }|€t        dt        ›d| ›�«      ‚dd l}ddlm}  |t        | g|¢­Ž  t         |j                  |d   «      |d   «      S )Nzmodule z has no attribute r   )Ú	warn_oncer\   )	Ú_PLUGIN_COMPAT_LAZYrr   ÚAttributeErrorrÜ   Ú	importlibÚhermes_cli.plugin_compatrY  r?   Úimport_module)r+   rd  r\  rY  s       r   Ú__getattr__r_  à  sh   € Ü ×$Ñ$ TÓ*€FØ€~Ü˜w¤x lÐ2DÀTÀHÐMÓNÐNÛÝ2ÙŒh˜Ð&˜vÓ&ÜÐ*�9×*Ñ*¨6°!©9Ó5°v¸a±yÓAÐAr   )F)r	   N)FNNNFNFFNFr   r   )«rß   rV  r]  r/   rw  rù   rh  ÚatexitÚdataclassesr   Útypingr   r   r   r   Ú	getLoggerrÜ   r3   r   r   Útools.registryr   Útools.terminal_tool_lifecycler   r   r   r   r   r   Útools.terminal_tool_configr   r   r   r   r   r    r!   r"   r#   Útools.terminal_tool_backendsr$   r%   r&   r'   r(   Útools.tool_backend_helpersr)   r*   r7   rŒ   rÏ  rU  ÚDISK_USAGE_WARNING_THRESHOLD_GBrä   r@   rB   rE   rI   rK   rR   rU   r  rV   ry   rb   rf   rt   r  rz   Útools.environments.baser{   ÚTERMINAL_TOOL_DESCRIPTIONr|   rà   r}   rç  r´   r~   rå  rf  r\  rŠ   r‹   r•   r–   r—   rœ   r˜   r¹   rž   r¡   r¤   r¯   r·   r»   r¿   rÄ   Ú	frozensetrË   rÌ   rÏ   rè   rê   r³   rð   rõ   rö   rú   r  r  rç   r  Útupler  rY  rb  rk  rr  r}  Úregisterrƒ  Útools.terminal_tool_guardsr„  r…  r†  r‡  rˆ  Útools.terminal_tool_backgroundr‰  rŠ  r‹  Útools.terminal_tool_resultrŒ  r‘  r•  r¢  ÚBaseExceptionr¨  rÿ  rª  r²  rÄ  rÆ  rÙ  rÓ  rÚ  rÞ  rë  rð  r  r!  r  r&  r  r-  r1  r2  ÚTERMINAL_SCHEMArD  ÚpathlibrJ  Úimportlib.utilr\  ÚplatformÚreÚshlexÚshutilÚstatÚ
subprocessrZ  r_  rA   r   r   ú<module>r|     sè	  ðòó& Û Û 	Û 
Û Û Û Ý !ß ,Ó ,à	ˆ×	Ñ	˜8Ó	$€ðR sð R¨só Rõ &÷÷ ÷÷ õ ÷õ ÷
 Uð ð ¨sð È3ó ñ" 0Ð0QÐSVÐX[Ð]fÓgÐ ñ #9Ð9SÐUZÐ\aÐckÓ"lÐ ð  �	—‘Ó!€ò9ò4ò%ò
 ð
5˜có 5ð9 #ó 9õð
¨sð °tó ð_ D¨¨c¨¡Nð _°tó _ð /4ñC˜sð C¨cð CØ'+ðCØ8<óCõ ?ð	Ð ð (*Ð �d˜3 ˜8‘nÓ )Ø#%€��S˜%�ZÑ Ó %ØˆI�N‰NÓ€	Ø-/€��c˜9Ÿ>™>Ð)Ñ*Ó /Ø%�y—~‘~Ó'Ð Ø€ØÐ ð "Ð Ø+˜YŸ^™^Ó-Ð ð*°°c¸3°h±ð *ÀDó *ð` 24Ð �T˜#˜t C¨ H™~Ð-Ñ.Ó 3ð  "€ˆd�3˜�8‰nÓ !Ø"�I—N‘NÓ$Ð ð &(Ð �D˜˜c˜‘NÓ 'Ø&˜	Ÿ™Ó(Ð ð	$ H¨S¡Mð 	$¸À¹ð 	$È$ó 	$ð? ¨#¡ð ?°8¸C±=ó ?ð, 3ð ,¨4ó ,ð Cð °#ð ¸(À3¹-ó ð:$¨ð $¸¸cÀ3¸h¹ó $ð:. có .ðM¨Cð MÀÈ#Áð MÐSWó Mð cð ¨có ñ %ò &ó Ð ðU h¨s¡mð U¸ó Uñ �$Ô÷&=ð &=ó ð&=ðR˜ó ð4¨4ó 4ð
,G¨°©ð ,G¸#ó ,Gð^ H¨S¡Mð °d¸3À¸8±nó ð( Ø&ØØñ	Ð ð-˜Cð -¨D°°c°©Nð -ÀDÈÈcÈÁNð -ÐWZó -ð¨#ð ¸À¹ó ð 4¨¨S¨¡>ð ¸HÀS¹Mð ÈhÐWZÉmó ð< %*Ð !ó-Cðb #&Ð9TÑUÐ ð #ð ¸ð À%ó ð:F˜˜c 3˜h™ó FòRò$òòlð* €‡��Ô  ðS¨#ð S°$ó S÷õ ÷ pÑ oÝ Að
¸tð 
Ðaeð 
Ðjoó 
ð" "&Ø"ò#à�c‰]ð#ð ð#ð ˜#‘ð	#ð
 �s‰mð#ð 	ó#ð> 13ÈDò 0�sð 0¨#ð 0¸HÀS¹Mð 0Ð^aó 0ð˜ð ¨3ó ô$'�	ô 'ð ÷	ð 	ó ð	ð! #ð !°ð !¸dÀ3ÈÀ8¹nð !ÐX\ð !Ðaqó !ðH ÷;ð ;ó ð;ðFð ðPØðPØ& s™mðPØ6>¸s±mðPàðPà#'ðPð óPðhið ð0 Sð 0¸Sð 0ÀSó 0ð/�yð /¨8°C©=ð /¸Só /ðdE˜3ð E¨$ó Eð8Øð8Øð8Ø"+ð8à�c‰]ð8à(0°©ð8àDGð8ð �c‰]ð8ð ,4°C©=ð8ð LPð8ð 	ó	8ð| !#Ð ð%Øð%Øð%Ø),ð%Ø36ð%à�c‰]ð%à),ð%ð 
ó%ð6ð ðÐ2ð ¸XÀc¹]ð Èsó ð. Ø!Ø!Ø $ØØ!ØØ$Ø*.ØØ$%Øñm$Øðm$àðm$ð �c‰]ðm$ð �c‰]ð	m$ð
 ˜‘ðm$ð ðm$ð �c‰]ðm$ð 
ðm$ð ðm$ð ˜T #™YÑ'ðm$ð ðm$ð "ðm$ð ðm$ð 	óm$ð`
 Tó 
õ $ð Ø,àð !Ø=ñð
 "ð  FØ ñð "Ø!TÐUkÐTlð  mnð  oEð  nFð  FEð   FØñð !Øñð
 "ð  qØ ñð  Eà˜YÐ'Ø$°¸Ð/AÑBðñð "Øð  ]ññ?'
ðP �KñU+ñ/€òd<ð~ €× Ñ Ø	ØØØØ(Ø
Ø!õõ Û Û Û 	Û Û Û Û Û 
ð BØ3ØIØ$bØ;Ø%dØOØ-tØ#`Ø"WñÐ óBr   