
    i&                        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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 ej                    dk    ZddlmZmZmZmZ  ej        e          Ze
j        dk    Z eg d          ZdZdZd	Zd
Zde fdZ!ddddddddZ"	 d?dee#         de#de#fdZ$dZ%de%z   dz   Z&de%z   dz   Z'h dZ(dej        de#d e)d!e)d"e*d#efd$Z+de#fd%Z,d&e#d'e#ddfd(Z-d)ede#fd*Z.d+e#de#d e)d!e)d"e*d#ed,ej/        fd-Z0d.e#dee#         deee#                  de#fd/Z1	 	 d@d.e#dee#         deee#                  de#fd0Z2dAd2e fd3Z3de4fd4Z5g d5Z6dBd6e7de4fd7Z8 e8            Z9dd8l:m;Z;m<Z<  e;j=        d9d:e9d; e!d<d=>           dS )Ca  
Code Execution Tool -- Programmatic Tool Calling (PTC)

Lets the LLM write a Python script that calls Hermes tools via RPC,
collapsing multi-step tool chains into a single inference turn.

Architecture (two transports):

  **Local backend (UDS):**
  1. Parent generates a `hermes_tools.py` stub module with UDS RPC functions
  2. Parent opens a Unix domain socket and starts an RPC listener thread
  3. Parent spawns a child process that runs the LLM's script
  4. Tool calls travel over the UDS back to the parent for dispatch

  **Remote backends (file-based RPC):**
  1. Parent generates `hermes_tools.py` with file-based RPC stubs
  2. Parent ships both files to the remote environment
  3. Script runs inside the terminal backend (Docker/SSH/Modal/Daytona/etc.)
  4. Tool calls are written as request files; a polling thread on the parent
     reads them via env.execute(), dispatches, and writes response files
  5. The script polls for response files and continues

In both cases, only the script's stdout is returned to the LLM; intermediate
tool results never enter the context window.

Platform: Linux / macOS only (Unix domain sockets for local). Disabled on Windows.
Remote execution additionally requires Python 3 in the terminal backend.
    NWindows)AnyDictListOptionalwin32)
web_searchweb_extract	read_file
write_filesearch_filespatchterminal,  2   iP  i'  returnc                      t           S )zCCode execution sandbox requires a POSIX OS for Unix domain sockets.)SANDBOX_AVAILABLE     A/home/agentuser/.hermes/hermes-agent/tools/code_execution_tool.pycheck_sandbox_requirementsr   I   s    r   )r	   zquery: str, limit: int = 5zS"""Search the web. Returns dict with data.web list of {url, title, description}."""z {"query": query, "limit": limit})r
   z
urls: listz`"""Extract content from URLs. Returns dict with results list of {url, title, content, error}."""z{"urls": urls})r   z,path: str, offset: int = 1, limit: int = 500zS"""Read a file (1-indexed lines). Returns dict with "content" and "total_lines"."""z0{"path": path, "offset": offset, "limit": limit})r   zpath: str, content: strzL"""Write content to a file (always overwrites). Returns dict with status."""z"{"path": path, "content": content})r   zpattern: str, target: str = "content", path: str = ".", file_glob: str = None, limit: int = 50, offset: int = 0, output_mode: str = "content", context: int = 0zr"""Search file contents (target="content") or find files by name (target="files"). Returns dict with "matches"."""z{"pattern": pattern, "target": target, "path": path, "file_glob": file_glob, "limit": limit, "offset": offset, "output_mode": output_mode, "context": context})r   zpath: str = None, old_string: str = None, new_string: str = None, replace_all: bool = False, mode: str = "replace", patch: str = Nonezt"""Targeted find-and-replace (mode="replace") or V4A multi-file patches (mode="patch"). Returns dict with status."""z|{"path": path, "old_string": old_string, "new_string": new_string, "replace_all": replace_all, "mode": mode, "patch": patch})r   z6command: str, timeout: int = None, workdir: str = NonezX"""Run a shell command (foreground only). Returns dict with "output" and "exit_code"."""z<{"command": command, "timeout": timeout, "workdir": workdir}udsenabled_tools	transportc                 b   t          t          t          |           z            }g }g }|D ]X}|t          vrt          |         \  }}}}	|                    d| d| d| d|d|	 d           |                    |           Y|dk    rt
          }
nt          }
|
d                    |          z   S )	ag  
    Build the source code for the hermes_tools.py stub module.

    Only tools in both SANDBOX_ALLOWED_TOOLS and enabled_tools get stubs.

    Args:
        enabled_tools: Tool names enabled in the current session.
        transport: ``"uds"`` for Unix domain socket (local backend) or
                   ``"file"`` for file-based RPC (remote backends).
    zdef (z):
    z
    return _call(, z)
file
)sortedSANDBOX_ALLOWED_TOOLSset_TOOL_STUBSappend_FILE_TRANSPORT_HEADER_UDS_TRANSPORT_HEADERjoin)r   r   tools_to_generatestub_functionsexport_names	tool_name	func_namesigdoc	args_exprheaders              r   generate_hermes_tools_moduler2      s    4s=7I7IIJJNL& 	' 	'	K'')4Y)?&	3Y>9 > >s > >> > )> >/8> > >	
 	
 	

 	I&&&&F'&DIIn----r   a  
# ---------------------------------------------------------------------------
# Convenience helpers (avoid common scripting pitfalls)
# ---------------------------------------------------------------------------

def json_parse(text: str):
    """Parse JSON tolerant of control characters (strict=False).
    Use this instead of json.loads() when parsing output from terminal()
    or web_extract() that may contain raw tabs/newlines in strings."""
    return json.loads(text, strict=False)


def shell_quote(s: str) -> str:
    """Shell-escape a string for safe interpolation into commands.
    Use this when inserting dynamic content into terminal() commands:
        terminal(f"echo {shell_quote(user_input)}")
    """
    return shlex.quote(s)


def retry(fn, max_attempts=3, delay=2):
    """Retry a function up to max_attempts times with exponential backoff.
    Use for transient failures (network errors, API rate limits):
        result = retry(lambda: terminal("gh issue list ..."))
    """
    last_err = None
    for attempt in range(max_attempts):
        try:
            return fn()
        except Exception as e:
            last_err = e
            if attempt < max_attempts - 1:
                time.sleep(delay * (2 ** attempt))
    raise last_err

z`"""Auto-generated Hermes tools RPC stubs."""
import json, os, socket, shlex, time

_sock = None
a  
def _connect():
    global _sock
    if _sock is None:
        _sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
        _sock.connect(os.environ["HERMES_RPC_SOCKET"])
        _sock.settimeout(300)
    return _sock

def _call(tool_name, args):
    """Send a tool call to the parent process and return the parsed result."""
    conn = _connect()
    request = json.dumps({"tool": tool_name, "args": args}) + "\n"
    conn.sendall(request.encode())
    buf = b""
    while True:
        chunk = conn.recv(65536)
        if not chunk:
            raise RuntimeError("Agent process disconnected")
        buf += chunk
        if buf.endswith(b"\n"):
            break
    raw = buf.decode().strip()
    result = json.loads(raw)
    if isinstance(result, str):
        try:
            return json.loads(result)
        except (json.JSONDecodeError, TypeError):
            return result
    return result

z"""Auto-generated Hermes tools RPC stubs (file-based transport)."""
import json, os, shlex, tempfile, time

_RPC_DIR = os.environ.get("HERMES_RPC_DIR") or os.path.join(tempfile.gettempdir(), "hermes_rpc")
_seq = 0
a  
def _call(tool_name, args):
    """Send a tool call request via file-based RPC and wait for response."""
    global _seq
    _seq += 1
    seq_str = f"{_seq:06d}"
    req_file = os.path.join(_RPC_DIR, f"req_{seq_str}")
    res_file = os.path.join(_RPC_DIR, f"res_{seq_str}")

    # Write request atomically (write to .tmp, then rename)
    tmp = req_file + ".tmp"
    with open(tmp, "w") as f:
        json.dump({"tool": tool_name, "args": args, "seq": _seq}, f)
    os.rename(tmp, req_file)

    # Wait for response with adaptive polling
    deadline = time.monotonic() + 300  # 5-minute timeout per tool call
    poll_interval = 0.05  # Start at 50ms
    while not os.path.exists(res_file):
        if time.monotonic() > deadline:
            raise RuntimeError(f"RPC timeout: no response for {tool_name} after 300s")
        time.sleep(poll_interval)
        poll_interval = min(poll_interval * 1.2, 0.25)  # Back off to 250ms

    with open(res_file) as f:
        raw = f.read()

    # Clean up response file
    try:
        os.unlink(res_file)
    except OSError:
        pass

    result = json.loads(raw)
    if isinstance(result, str):
        try:
            return json.loads(result)
        except (json.JSONDecodeError, TypeError):
            return result
    return result

>   pty
backgroundwatch_patternsnotify_on_completeserver_socktask_idtool_call_logtool_call_countermax_tool_callsallowed_toolsc                 ,
   ddl m} d}	 |                     d           |                                 \  }}|                    d           d}		 	 |                    d          }
n# t
          j        $ r Y nw xY w|
sn|	|
z  }	d	|	v r|	                    d	d
          \  }}	|                                }|s5t          j
                    }	 t          j        |                                          }n_# t          j        t          f$ rF}t!          d|           }|                    |dz                                              Y d}~d}~ww xY w|                    dd          }|                    di           }||vrjd                    t+          |                    }t          j        dd| d| i          }|                    |dz                                              i|d         |k    rFt          j        dd| di          }|                    |dz                                              |dk    r5t/          |t0                    r t2          D ]}|                    |d           	 t6          j        t6          j        }}t=          t>          j         d          }	 |t6          _        |t6          _         ||||          }||ct6          _        t6          _        |!                                 n2# ||ct6          _        t6          _        |!                                 w xY wnP# tD          $ rC}tF          $                    d|d           t!          tK          |                    }Y d}~nd}~ww xY w|dxx         d
z  cc<   t          j
                    |z
  }tK          |          dd         }|&                    ||tO          |d          d           |                    |dz                                              d	|	v n^# t
          j        $ r tF          (                    d           Y n3tR          $ r'}tF          (                    d|d           Y d}~nd}~ww xY w|rJ	 |!                                 dS # tR          $ r&}tF          (                    d |           Y d}~dS d}~ww xY wdS # |rH	 |!                                 w # tR          $ r%}tF          (                    d |           Y d}~w d}~ww xY ww xY w)!z
    Accept one client connection and dispatch tool-call requests until
    the client disconnects or the call limit is reached.
    r   handle_function_callN   r   r   Ti      
   zInvalid RPC request: r    tool argsr   errorTool '/' is not available in execute_code. Available: Tool call limit reached (0). No more tool calls allowed in this execution.r   wr8   zTool call failed in sandbox: %sexc_infoP      rC   args_previewdurationzRPC listener socket timeoutzRPC listener socket error: %szRPC conn close error: %s)*model_toolsr?   
settimeoutacceptrecvsockettimeoutsplitstriptime	monotonicjsonloadsdecodeJSONDecodeErrorUnicodeDecodeError
tool_errorsendallencodegetr(   r!   dumps
isinstancedict_TERMINAL_BLOCKED_PARAMSpopsysstdoutstderropenosdevnullclose	ExceptionloggerrF   strr%   rounddebugOSError)r7   r8   r9   r:   r;   r<   r?   conn_bufchunkline
call_startrequestexcrespr,   	tool_args	availableparam_real_stdout_real_stderrrq   resultcall_durationrR   es                              r   _rpc_server_loopr   3  s    100000Df<q!!!$$&&aU	7		%((>    5LC 3,,IIeQ//	czz|| !^--
"j77GG,.@A   %&Cc&C&CDDDLL$+!5!5!7!7888HHHH
 $KK33	#KK33	 M11 $		&*?*? @ @I:6Y 6 6*36 6'  D LL$+!5!5!7!7888 %Q'>99:L L L L'  D LL$+!5!5!7!7888 
**z)T/J/J*!9 3 3!eT2222
214SZ,L"2:s33G(%,
%,
!5!5%y'" " " 2>|.
CJ 2>|.
CJ  2 2 2LL!BCRVLWWW'C11FFFFFF2 "!$$$)$$$ $ 0 0: =  #9~~crc2$$%$0 %mQ 7 7& &    ftm3355666W 3,,U	7n > 4 4 4233333 H H H4a$GGGGGGGGH  	<<

 < < <7;;;;;;;;;<	< 	<4 	<<

 < < <7;;;;;;;;<	<s  AP A% $P %A84P 7A88AP &C4 3P 4E
<EP ED*P ;2L5 .&L .L5 /L11L5 4P 5
N?9M=8P =NBP S )Q6S 	Q6Q1,S 1Q66S <R 
SR==STS T 
T*T
T
TTc                    ddl m}m}m}m}m}m}m}m}m	}	 | pd}
|5  |
|v r:t          j
                    ||
<   ||
          |            d         fcddd           S 	 ddd           n# 1 swxY w Y   |5  |
|vrt          j                    ||
<   ||
         }ddd           n# 1 swxY w Y   |5  |5  |
|v rFt          j
                    ||
<   ||
          |            d         fcddd           cddd           S 	 ddd           n# 1 swxY w Y    |            }|d         }|	                    |
i           }|dk    r|                    d          p|d         }nn|dk    r|                    d	          p|d	         }nJ|d
k    r|                    d          p|d         }n&|dk    r|                    d          p|d         }nd}|                    d          p|d         }d}|dv rl|                    dd          |                    dd          |                    dd          |                    dd          |                    dg           d}d}|dk    rl|                    dd          |                    dd          |                    dd          |                    d d          |                    d!d"          d#}d}|d$k    rd%|                    d&d"          i}t                              d'||
dd(                     |||||d)         ||||
|                    d*          +	  	        }|5  |||
<   t          j
                    ||
<   ddd           n# 1 swxY w Y    |             t                              d,||
dd(                    ||fcddd           S # 1 swxY w Y   dS )-zGet or create the terminal environment for *task_id*.

    Reuses the same environment (container/sandbox/SSH session) that the
    terminal and file tools use, creating one if it doesn't exist yet.
    Returns ``(env, env_type)`` tuple.
    r   )	_active_environments	_env_lock_create_environment_get_env_config_last_activity_start_cleanup_thread_creation_locks_creation_locks_lock_task_env_overridesdefaultenv_typeNdockerdocker_imagesingularitysingularity_imagemodalmodal_imagedaytonadaytona_imagerD   cwd)r   r   r   r   container_cpurB   container_memoryi   container_diski   container_persistentTdocker_volumes)r   r   r   r   r   sshssh_hostssh_userssh_port   ssh_keyssh_persistentF)hostuserportkey
persistentlocalr   local_persistentz7Creating new %s environment for execute_code task %s...   rY   host_cwd)	r   imager   rY   
ssh_configcontainer_configlocal_configr8   r   z-%s environment ready for execute_code task %s)tools.terminal_toolr   r   r   r   r   r   r   r   r   r\   	threadingLockrf   rt   info)r8   r   r   r   r   r   r   r   r   r   effective_task_id	task_lockconfigr   	overridesr   r   r   r   r   envs                        r   _get_or_create_envr     s                          ,9 
 Z Z 44404	N,-'(9:OO<M<Mj<YYZ Z Z Z Z Z Z Z4Z Z Z Z Z Z Z Z Z Z Z Z Z Z Z 
 7 7O331:1A1AO-.#$56	7 7 7 7 7 7 7 7 7 7 7 7 7 7 7
 
 F F 	^ 	^ $88848IKK01+,=>@Q@QR\@]]	^ 	^ 	^ 	^ 	^ 	^ 	^F F F F F F F F8	^ 	^ 	^ 	^ 	^ 	^ 	^ 	^ 	^ 	^ 	^ 	^ 	^ 	^ 	^
 !""*%'++,=rBB	xMM.11KVN5KEE&&MM"566U&AT:UEE  MM-00IF=4IEE""MM/22Mf_6MEEEmmE""3fUmDDD!'OQ!?!?$*JJ/A4$H$H"(**-=u"E"E(.

3I4(P(P"(**-=r"B"B    
u

:r22

:r22

:r22zz)R00$jj)95AA J wfjj);UCCL 	M0!4	6 	6 	6!!9%!-%%ZZ
++

 

 

  	< 	<69 !2304	N,-	< 	< 	< 	< 	< 	< 	< 	< 	< 	< 	< 	< 	< 	< 	< 	C0!4	6 	6 	6H}MF F F F F F F F F F F F F F F F F Fs   2A))A-0A-6#B%%B),B)2N952D'N9N9D	N9D	H3N9M0$N90M4	4N97M4	84N99N= N=remote_pathcontentc                     t          j        |                    d                                        d          }t	          j        |          }|                     d| d| dd           dS )	u&  Write *content* to *remote_path* on the remote environment.

    Uses ``echo … | base64 -d`` rather than stdin piping because some
    backends (Modal) don't reliably deliver stdin_data to chained
    commands.  Base64 output is shell-safe ([A-Za-z0-9+/=]) so single
    quotes are fine.
    utf-8asciiecho '' | base64 -d > /   r   rY   N)base64	b64encodere   r`   shlexquoteexecute)r   r   r   encodedquoted_remote_paths        r   _ship_file_to_remoter     s}     w~~g6677>>wGGG[11KK>>>*<>>      r   r   c                    t          | dd          }t          |          r	  |            }t          |t                    r,|                    d          r|                    d          pdS n2# t          $ r%}t                              d|           Y d}~nd}~ww xY wt          j
                    }t          |t                    r,|                    d          r|                    d          pdS dS )zAReturn a writable temp dir for env-backed execute_code sandboxes.get_temp_dirNr   z/Could not resolve execute_code env temp dir: %s/tmp)getattrcallablerh   ru   
startswithrstriprs   rt   rw   tempfile
gettempdir)r   r   temp_dirr   	candidates        r   _env_temp_dirr   $  s   355L Q	Q#|~~H(C(( 3X-@-@-E-E 3s++2s2 	Q 	Q 	QLLJCPPPPPPPP	Q#%%I)S!! ,i&:&:3&?&? ,$$++6s   A
A. .
B8BBrpc_dir
stop_eventc                  
   ddl m} d}	t          j        |          }
|                                s	 |                     d|
 ddd          }|                    d	d
                                          }|s|                    |	           rt          d |
                    d          D                       }|D ]}|                                r nt          j                    }t          j        |          }|                     d| dd          }	 t          j        |                    d	d
                    }nR# t          j        t           f$ r9 t"                              d|           |                     d| dd           Y w xY w|                    dd
          }|                    di           }|                    dd          }|d}| d| }t          j        |          }||vr@d                    t          |                    }t          j        dd| d| i          }n|d         |k    rt          j        dd| di          }n|dk    r5t+          |t,                    r t.          D ]}|                    |d           	 t2          j        t2          j        }}t9          t:          j        d          }	 |t2          _        |t2          _         ||||          }||ct2          _        t2          _        |                                 n2# ||ct2          _        t2          _        |                                 w xY wnP# t@          $ rC}t"          !                    d |d!"           tE          tG          |                    }Y d}~nd}~ww xY w|dxx         d#z  cc<   t          j                    |z
  } |$                    |tG          |          dd$         tK          | d%          d&           tM          j'        |(                    d'                    )                    d(          }!|                     d)|! d*| d+| d,| dd-           |                     d| dd           nH# t@          $ r;}"|                                st"                              d.|"d!"           Y d}"~"nd}"~"ww xY w|                                s|                    |	           |                                dS dS )/zPoll the remote filesystem for tool call requests and dispatch them.

    Runs in a background thread.  Each ``env.execute()`` spawns an
    independent process, so these calls run safely concurrent with the
    script-execution thread.
    r   r>   g?zls -1 z/req_* 2>/dev/null || truer   
   r   outputrD   c                     g | ]g}|                                 rQ|                                                     d           s*d|                                 v S|                                 hS )z.tmpz/req_)r[   endswith).0fs     r   
<listcomp>z"_rpc_poll_loop.<locals>.<listcomp>V  sq          7799  		**622  qwwyy(( 		 )((r   r    zcat zMalformed RPC request in %szrm -f r@   rC   rE   seq06dz/res_r   rF   rG   rH   rI   rJ   r   NrK   rL   z&Tool call failed in remote sandbox: %sTrM   rB   rO   rP   rQ   r   r   r   r   z.tmp && mv z.tmp <   zRPC poll error: %s)*rT   r?   r   r   is_setr   rf   r[   waitr!   rZ   r\   r]   r^   r_   ra   
ValueErrorrt   rw   r(   rg   rh   ri   rj   rk   rl   rm   rn   ro   rp   rq   rr   rs   rF   rc   ru   r%   rv   r   r   re   r`   )#r   r   r8   r9   r:   r;   r<   r   r?   poll_intervalquoted_rpc_dir	ls_resultr   	req_filesreq_filer~   quoted_req_fileread_resultr   r,   r   r   seq_strres_filequoted_res_filer   tool_resultr   r   r   rq   r   r   encoded_resultr   s#                                      r   _rpc_poll_loopr   4  s     100000M[))N!! v+r	ECCCC $  I
 ]]8R006688F ...    #)<<#5#5       I & [L [L$$&& E!^--
"'+h"7"7!kk,?,, *  
"j2)F)FGGGG,j9   LL!>IIIKK : : :QKOOOH	 $KK33	#KK33	kk%++ ,,%55G55"'+h"7"7 M11 $		&*?*? @ @I"&*6Y 6 6*36 6. # #KK 'q)^;;"&*L L L L. # #KK !J..:i3N3N.%= 7 7E%MM%6666;58Zl"&rz3"7"7,)0CJ)0CJ*>*> )9g+ + +K 6B<2CJ
#MMOOOO 6B<2CJ
#MMOOOOO$ ; ; ;%M%(4 % 9 9 9&0S&:&:;
 &a(((A-((($(N$4$4z$AM!(( )(+Iss(;$)-$;$;* *    "(!1&&w//" "&//  F^ F F_ F F-F F4CF F	     6_66CKKKK 	E 	E 	E$$&& E11tDDD	E   "" 	+OOM***m !! v+ v+ v+ v+ v+s   AR B
R (ER AFR FDR 2M&L%7.M%/MMR 
N%"9N R  N%%C R 
S1SScodec                    t                      }|                    dt                    }|                    dt                    }|rt	          |          nt	                      }t          t          |z            }|st          }|pd}t          |          \  }	}
t          j	                    j
        dd         }t          |	          }| d| }t          j        |          }t          j        | d          }g }dg}t          j                    }t!          j                    }d}	 |	                    d	d
d          }d|                    dd          vrt'          j        dd|
 dddd          |                                 ||                    d           	 |	                    d| d
d           S # t,          $ r t.                              d|           Y S w xY w|	                    d| d
d           t3          t5          |          d          }t7          |	| d|           t7          |	| d|            t!          j        t:          |	| d||||||fd          }|                                 d t          j        | d           d!}t?          j         d"d          !                                }|r|d#| z  }t.          "                    d$|
|dd%                    |	                    d&| d'| d(|          }|                    dd          }|                    d)d*          }d+}|d,k    rd}n|d-k    rd.}n# t,          $ r}tG          t          j                    |z
  d/          }t.          $                    d0||d         tK          |          j&        |d1           t'          j        dtO          |          |d         |dd23          cY d}~|                                 ||                    d           	 |	                    d| d
d           S # t,          $ r t.                              d|           Y S w xY wd}~ww xY w|                                 ||                    d           	 |	                    d| d
d           n# t,          $ r t.                              d|           Y n|w xY w# |                                 ||                    d           	 |	                    d| d
d           w # t,          $ r t.                              d|           Y w w xY wxY wtG          t          j                    |z
  d/          }tQ          |          tR          k    rtU          tR          d4z            }tR          |z
  } |d|         }!||  d         }"tQ          |          tQ          |!          z
  tQ          |"          z
  }#|!d5|#d6d7tQ          |          d6d8z   |"z   }dd9l+m,}$  |$|          }dd:l-m.}%  |%|          }|||d         |d;}&|dk    rEd<| d=}'|'|&d<   |r|d>|' z   |&d<   nd?|' |&d<   t.          /                    d@|||d                    n"|d.k    r	|dAz   |&d<   n|dk    rd|&dB<   dC| |&d<   t'          j        |&d23          S )Da  Run a script on the remote terminal backend via file-based RPC.

    The script and the generated hermes_tools.py module are shipped to
    the remote environment, and tool calls are proxied through a polling
    thread that communicates via request/response files.
    rY   r;   r   N   z/hermes_exec_z/rpcr   z-command -v python3 >/dev/null 2>&1 && echo OKr      r   OKr   rD   rF   z!Python 3 is not available in the zO terminal environment. Install Python to use execute_code with remote backends.statusrF   tool_calls_madeduration_secondsr@   rY   zrm -rf z$Failed to clean up remote sandbox %sz	mkdir -p r   r   )r   z/hermes_tools.pyz
/script.pyTtargetrE   daemonzHERMES_RPC_DIR=z PYTHONDONTWRITEBYTECODE=1HERMES_TIMEZONEz TZ=z)Executing code on %s backend (task %s)...r   zcd z && z python3 script.py
returncodesuccess|      interruptedrP   z?execute_code remote failed after %ss with %d tool calls: %s: %srM   Fensure_ascii皙?

... [OUTPUT TRUNCATED - , chars omitted out of  total] ...


strip_ansiredact_sensitive_textr  r   r  r  Script timed out after s and was killed.   

⏰    ⏰ zHexecute_code (remote) timed out after %ss (limit %ss) with %d tool calls4   
[execution interrupted — user sent a new message]r  Script exited with code )0_load_configrf   DEFAULT_TIMEOUTDEFAULT_MAX_TOOL_CALLSr#   	frozensetr"   r   uuiduuid4hexr   r   r   r\   r]   r   Eventr   r^   rg   r(   rs   rt   rw   r2   listr   Threadr   startrp   getenvr[   r   rv   rF   type__name__ru   lenMAX_STDOUT_BYTESinttools.ansi_stripr  agent.redactr  warning)(r   r8   r   _cfgrY   r;   session_toolssandbox_toolsr   r   r   
sandbox_idr   sandbox_dirquoted_sandbox_dirr   r9   r:   
exec_startr   
rpc_threadpy_check	tools_src
env_prefixtzscript_resultstdout_text	exit_coder  r   rS   
head_bytes
tail_bytesheadtailomittedr  r  r   timeout_msgs(                                           r   _execute_remoterM    s    >>Dhhy/22GXX.0FGGN*7BC&&&SUUM3mCDDM .-,9&'899MC!#2#&JS!!H88J88K[11[K!5!5!566NM!!J""JJ_N;;;R  
 
 x||Hb1111:!' ' ' ' $%$%	 	 	 	\ 	!OOAO&&&	NKK.,..C       	N 	N 	NLL?MMMMM	NW 	(((c2 	 	
 	
 	

 16
 
 
	 	S["B"B"BINNNS["<"<"<dCCC %!)))+<0.z
 
 
 

 	)ek[*>*>*>?? ) ) ) 	 Y("--3355 	&+++%J 	?0!4	6 	6 	6H$HH*HHH $ 
 

 $''"55!%%lB77	 FF#"F   ))J6::M'*DII,> 	 	
 	
 	

 zXX03 (	
 

    	 	 	 	 	 	!OOAO&&&	NKK.,..C       	N 	N 	NLL?MMMMM	N3  	!OOAO&&&	NKK.,..C       	N 	N 	NLL?MMMMM	N 	!OOAO&&&	NKK.,..C       	N 	N 	NLL?MMMMM	N T^%%
2A66H
 ;***)C/00
%
2
;J;'J;<<(k""SYY.T::WG : :+&&9: : ::  	 ,+++++*[))K 322222''44K ,Q/$	 F JJJJ%w  	4*-E-E-EEF83k33F8Vg03	
 	
 	
 	
 
=	 	 QQ 	x 
a"x@Y@@w:f51111s   )AM "F>>%G&%G&)E)M S Q&B	Q!(Q&)S P66%QQ!Q&&S R2 2%SS-UT'&U'%UUUUc           	      R  > t           st          j        ddi          S | r|                                 st	          d          S ddlm}  |            d         }|dk    rt          | ||          S ddlm	} t                      }|                    d	t                    }|                    d
t                    }|rt          |          nt                      }	t          t           |	z            }
|
st           }
t#          j        d          }t&          j        dk    rdnt#          j                    }t,          j                            |dt3          j                    j         d          }g }dg}t9          j                    }d}	 t=          t?          |
                    }tA          t,          j                            |d          d          5 }|!                    |           ddd           n# 1 swxY w Y   tA          t,          j                            |d          d          5 }|!                    |            ddd           n# 1 swxY w Y   tE          j"        tD          j#        tD          j$                  }|%                    |           |&                    d           tO          j(        tR          ||||||
fd          }|*                                 d}d}	 ddl+m,} n# tZ          $ r d }Y nw xY wi }t,          j.        /                                D ]R\  >} |>          r||><   ta          >fd|D                       r2ta          >fd|D                       r||><   S||d<   d|d <   t,          j        1                    t,          j        1                    t,          j        2                    tf                                        }|                    d!d"          }||rt,          j4        |z   nd"z   |d!<   t-          j5        d#d"                                          }|r||d$<   |6                    d#d           dd%l7m8}  |            }|r||d&<   ts          j:        t&          j;        dg||tr          j<        tr          j<        tr          j=        t|          rdnt,          j?        '          }t9          j                    |z   } g }!t          t          d(z            }"t          |"z
  }#d) }$dg}%d* }&g }'g }(tO          j(        |&|jB        |'|(|"|#|%fd          })tO          j(        |$|jC        |!t          fd          }*|)*                                 |**                                 d+}+t9          j                    |d,},|E                                 |            rt          |           d-}+nwt9          j                    | k    rt          |d.           d	}+nL	 dd/lGmH}-  |-|,d0           n# tZ          $ r Y nw xY wt9          jI        d1           |E                                |)                    d23           |*                    d23           d4                    |'          J                    d5d67          }.d4                    |(          J                    d5d67          }/d4                    |!          J                    d5d67          }0|%d         }1|1t          k    r8|/r6|1t          |.          z
  t          |/          z
  }2d8|2d9d:|1d9d;}3|.|3z   |/z   }4n|.|/z   }4|jL        |jL        nd<}5t          t9          j                    |z
  d=          }6|N                                 d}|                    d23           dd>lOmP}7  |7|4          }4 |7|0          }0dd?lQmR}8  |8|4          }4 |8|0          }0|+|4|d         |6d@}9|+d	k    rEdA| dB}:|:|9d<   |4r|4dC|: z   |9dD<   ndE|: |9dD<   t          T                    dF|6||d                    n1|+d-k    r	|4dGz   |9dD<   n"|5dk    rd|9dH<   |0pdI|5 |9d<   |0r|4dJz   |0z   |9dD<   t          j        |9dKL          |H	 |N                                 n2# t          $ r%};t          V                    dM|;           Y d};~;nd};~;ww xY wddlW}<|<X                    |dN           	 t-          jY        |           S # t          $ r Y S w xY w# tZ          $ r}=t          t9          j                    |z
  d=          }6t          Z                    dO|6|d         t          |=          j\        |=dP           t          j        dt          |=          |d         |6dQdKL          cY d}=~=|H	 |N                                 n2# t          $ r%};t          V                    dM|;           Y d};~;nd};~;ww xY wddlW}<|<X                    |dN           	 t-          jY        |           S # t          $ r Y S w xY wd}=~=ww xY w# |H	 |N                                 n2# t          $ r%};t          V                    dM|;           Y d};~;nd};~;ww xY wddlW}<|<X                    |dN           	 t-          jY        |           w # t          $ r Y w w xY wxY w)RaB  
    Run a Python script in a sandboxed child process with RPC access
    to a subset of Hermes tools.

    Dispatches to the local (UDS) or remote (file-based RPC) path
    depending on the configured terminal backend.

    Args:
        code:          Python source code to execute.
        task_id:       Session task ID for tool isolation (terminal env, etc.).
        enabled_tools: Tool names enabled in the current session. The sandbox
                       gets the intersection with SANDBOX_ALLOWED_TOOLS.

    Returns:
        JSON string with execution results.
    rF   zHexecute_code is not available on Windows. Use normal tool calls instead.zNo code provided.r   )r   r   r   )is_interruptedrY   r;   hermes_sandbox_)prefixdarwinr   hermes_rpc_z.sockNzhermes_tools.pyrK   z	script.pyrB   Tr  )PATHHOMEUSERLANGLC_TERMTMPDIRTMPTEMPSHELLLOGNAMEXDG_
PYTHONPATHVIRTUAL_ENVCONDAHERMES_)KEYTOKENSECRETPASSWORD
CREDENTIALPASSWDAUTH)is_env_passthroughc                     dS )NFr   )rz   s    r   <lambda>zexecute_code.<locals>.<lambda>  s     r   c              3   D   K   | ]}|                                 v V  d S N)upper)r   sks     r   	<genexpr>zexecute_code.<locals>.<genexpr>  s/      >>a1		>>>>>>>r   c              3   B   K   | ]}                     |          V  d S ro  )r   )r   prr  s     r   rs  zexecute_code.<locals>.<genexpr>  s-      ??q1<<????????r   HERMES_RPC_SOCKET1PYTHONDONTWRITEBYTECODEr`  rD   r  TZ)get_subprocess_homerU  )r   r   rm   rn   stdin
preexec_fnr  c                 *   d}	 	 |                      d          }|sdS ||k     r"||z
  }|                    |d|                    |t          |          z  }T# t          t          f$ r(}t
                              d|d           Y d}~dS d}~ww xY w)z)Simple head-only drain (used for stderr).r   T   Nz Error reading process output: %srM   )readr%   r2  r   rx   rt   rw   )pipechunks	max_bytestotaldatakeepr   s          r   _drainzexecute_code.<locals>._drain*  s    E
S'99T??D y(((50d5D5k222SYY&E' ( S S S?TRRRRRRRRRSs   A ;A B*BBc                 \   d}ddl m}  |            }d}		 	 |                     d          }
|
sn|dxx         t          |
          z  cc<   ||k     rOt	          t          |
          ||z
            }|                    |
d|                    ||z  }|
|d         }
|
s|                    |
           |	t          |
          z  }	|	|k    r0|r.|                                }|	t          |          z  }	|	|k    r|.n# t          t          f$ r Y nw xY w|	                    |           dS )z-Drain stdout keeping both head and tail data.r   )dequeTr~  N)
collectionsr  r  r2  minr%   popleftr   rx   extend)r  head_chunkstail_chunksrG  rH  	total_refhead_collectedr  tail_buftail_collectedr  r  oldests                r   _drain_head_tailz&execute_code.<locals>._drain_head_tail;  s~   N))))))uwwHN699T??D aLLLCII-LLL%
22"3t99j>.IJJ#**4;777&$.#DEE{# %$OOD)))"c$ii/N(:55(5!)!1!1!3!3&#f++5 ):55(5#6 " (    x(((((s   C)D   DDr  )
last_touchr.  r  )escalate)touch_activity_if_duezexecute_code runningg?   r  r   r   replace)errorsr  r  r  r  r  rP   r  r  r  r  r  r   r   r!  z?execute_code timed out after %ss (limit %ss) with %d tool callsr"  r  r#  z
--- stderr ---
Fr  zServer socket close error: %s)ignore_errorsz8execute_code failed after %ss with %d tool calls: %s: %srM   r  )^r   r^   rg   r[   rc   r   r   rM  tools.interruptrO  r$  rf   r%  r&  r#   r'  r"   r   mkdtemprl   platformr   rp   pathr(   r(  r)  r*  r\   r]   r2   r,  ro   writerX   AF_UNIXSOCK_STREAMbindlistenr   r-  r   r.  tools.env_passthroughrk  rs   environitemsanydirnameabspath__file__pathsepr/  rk   hermes_constantsrz  
subprocessPopen
executablePIPEDEVNULL_IS_WINDOWSsetsidr4  r3  rm   rn   MAX_STDERR_BYTESpoll_kill_process_grouptools.environments.baser  sleepr`   r2  r  rv   rr   r5  r  r6  r  rt   r7  rx   rw   shutilrmtreeunlinkrF   r0  r1  ru   )?r   r8   r   r   r   _is_interruptedr8  rY   r;   r9  r:  tmpdir_sock_tmpdir	sock_pathr9   r:   r>  r7   rA  r   r?  _SAFE_ENV_PREFIXES_SECRET_SUBSTRINGS_is_passthrough	child_envv_hermes_root_existing_pp_tz_namerz  _profile_homeprocdeadlinestderr_chunks_STDOUT_HEAD_BYTES_STDOUT_TAIL_BYTESr  stdout_total_bytesr  stdout_head_chunksstdout_tail_chunksstdout_readerstderr_readerr  _activity_stater  stdout_headstdout_tailstderr_texttotal_stdoutrK  truncated_noticerE  rF  rS   r  r  r   rL  r   r  r   rr  s?                                                                 @r   execute_coder    st   *  z_
   	  /tzz|| /-... 433333  ,H7tWm<<<
 BAAAAA >>Dhhy/22GXX.0FGGN +8BC&&&SUUM3mCDDM .- %6777F !\X55668;N;P;PL\+P9I+P+P+PQQIM!!JKd 1m1D1DEE	"',,v'8993?? 	1GGI	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 "',,v{33S99 	QGGDMMM	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 mFNF4FGG###1%#Wm!>= 
 
 

 	)0	.SSSSSSS 	. 	. 	.-oOOO	.	J$$&& 
	! 
	!DAqq!!  	!>>>>+=>>>>> ????,>????? ! 	!)2	%&/2	+, wrwrwx7P7P'Q'QRR }}\266".|2c"*|2K2Kac"d	,
 9.3399;; 	'&IdO'... 	988888++-- 	. -If^[)??$*9tt	
 
 
 >##g-  !!1C!788-0BB	S 	S 	S  S	) 	) 	)@ $&#%!(#+13E$&8:LN	
 
 
 "(m=M NW[
 
 
 	.**
 
 iikk!   #D)))&~(**#D48888"IIIIII%%o7MNNNN   JsOOO! iikk!& 	1%%%1%%%hh12299')9TThh12299')9TThh}--44WY4OO *!,***{*"S%5%55K8H8HHG:wI : :&9: : :  &(88;FKK%3K'+'BDOO	))J6:: 	""" 	0///// j-- j-- 	766666++K88++K88 !03 (	"
 "
 YNGNNNK)F7O
  8#.1IK1I1I#Ix  #7+#7#7x NNQ'#4Q#7    }$$*-ddF8!^^&F8)S-S	-S-SF7O T#.1E#E#Sx z&u555* "A!!#### A A A<a@@@@@@@@AfD111	Ii     	 	 	D	9    ))J6::Fa II 	 	
 	
 	
 zXX03 (	
 

    	 	 	 	 	 "A!!#### A A A<a@@@@@@@@AfD111	Ii     	 	 	D	9& "A!!#### A A A<a@@@@@@@@AfD111	Ii     	 	 	D	sg  	A
c. G5)c. 5G99c. <G9=1c. .Ic. Ic. IBc. )K0 /c. 0L =c. ?L  K'c. (W; :c. ;
Xc. XIc. &a;;
b*b%%b*	c
c+*c+.h9B	hhh 
f
g)g		g-h
hhhh j&h32j&3
i"=ij&i""j&jj&
j# j&"j##j&Fr  c                    	 t           r|                                  n6t          j        t          j        | j                  t          j                   n# t          t          f$ rq}t                              d|d           	 |                                  n4# t          $ r'}t                              d|d           Y d}~nd}~ww xY wY d}~nd}~ww xY w|r	 |                     d           dS # t          j        $ r 	 t           r|                                  n9t          j        t          j        | j                  t          j                   Y dS Y dS # t          t          f$ rz}t                              d|d           	 |                                  n4# t          $ r'}t                              d|d           Y d}~nd}~ww xY wY d}~Y dS Y d}~Y dS d}~ww xY ww xY wdS )	z,Kill the child and its entire process group.z Could not kill process group: %sTrM   zCould not kill process: %sNr@   r  z-Could not kill process group with SIGKILL: %s)r  	terminaterp   killpggetpgidpidsignalSIGTERMProcessLookupErrorPermissionErrorrt   rw   killrs   r   r  TimeoutExpiredSIGKILL)r  r  r   e2s       r   r  r    s]   
J 	<NNIbj**FN;;;0 J J J7TJJJ	JIIKKKK 	J 	J 	JLL5rDLIIIIIIII	J	J  R	RIIaI     ( 	R 	R 	R
R DIIKKKKIbj22FNCCCCCC  KK '8 R R RLaZ^___RIIKKKK  R R RLL!=rDLQQQQQQQQR  KKKKKKQQQQQQQR	R	R Rs   AA C&CBC
C
#C CC

CCC6 6G-AEG)/G$F"!G$"
G,G	G$GG$G-G-$G))G-c                  `    	 ddl m}  |                     di           S # t          $ r i cY S w xY w)z8Load code_execution config from CLI_CONFIG if available.r   
CLI_CONFIGcode_execution)clir  rf   rs   r  s    r   r$  r$    sR    """"""~~.333   			s    --))r	   zv  web_search(query: str, limit: int = 5) -> dict
    Returns {"data": {"web": [{"url", "title", "description"}, ...]}})r
   z  web_extract(urls: list[str]) -> dict
    Returns {"results": [{"url", "title", "content", "error"}, ...]} where content is markdown)r   z  read_file(path: str, offset: int = 1, limit: int = 500) -> dict
    Lines are 1-indexed. Returns {"content": "...", "total_lines": N})r   zT  write_file(path: str, content: str) -> dict
    Always overwrites the entire file.)r   z  search_files(pattern: str, target="content", path=".", file_glob=None, limit=50) -> dict
    target: "content" (search inside files) or "files" (find files by name). Returns {"matches": [...]})r   z  patch(path: str, old_string: str, new_string: str, replace_all: bool = False) -> dict
    Replaces old_string with new_string in the file.)r   z  terminal(command: str, timeout=None, workdir=None) -> dict
    Foreground only (no background/pty). Returns {"output": "...", "exit_code": N}enabled_sandbox_toolsc                      t            d                     fdt          D                       } fddD             }|st                     dd         }|rd                    |          dz   }nd	}d
| d}d|dddd| ddidgddS )u6  Build the execute_code schema with description listing only enabled tools.

    When tools are disabled via ``hermes tools`` (e.g. web is turned off),
    the schema description should NOT mention web_search / web_extract —
    otherwise the model thinks they are available and keeps trying to use them.
    Nr    c              3   *   K   | ]\  }}|v 	|V  d S ro  r   )r   namer/   r  s      r   rs  z,build_execute_code_schema.<locals>.<genexpr>@  s;        c8M0M0M0M0M0M0M r   c                     g | ]}|v |	S r   r   )r   nr  s     r   r   z-build_execute_code_schema.<locals>.<listcomp>E  s$    [[[QEZ@Z@Zq@Z@Z@Zr   )r	   r   rP   r   z, ...z...a,  Run a Python script that can call Hermes tools programmatically. Use this when you need 3+ tool calls with processing logic between them, need to filter/reduce large tool outputs before they enter your context, need conditional branching (if X then Y else Z), or need to loop (fetch N pages, process N files, retry on failure).

Use normal tool calls instead when: single tool call with no processing, you need to see the full result and apply complex reasoning, or the task requires interactive user input.

Available via `from hermes_tools import ...`:

uv  

Limits: 5-minute timeout, 50KB stdout cap, max 50 tool calls per script. terminal() is foreground-only (no background or pty).

Print your final result to stdout. Use Python stdlib (json, re, math, csv, datetime, collections, etc.) for processing between tool calls.

Also available (no import needed — built into hermes_tools):
  json_parse(text: str) — json.loads with strict=False; use for terminal() output with control chars
  shell_quote(s: str) — shlex.quote(); use when interpolating dynamic strings into shell commands
  retry(fn, max_attempts=3, delay=2) — retry with exponential backoff for transient failuresr  objectr   stringzDPython code to execute. Import tools with `from hermes_tools import z(` and print your final result to stdout.)r0  description)r0  
propertiesrequired)r  r  
parameters)r"   r(   _TOOL_DOC_LINESr!   )r  
tool_linesimport_examples
import_strr  s   `    r   build_execute_code_schemar  5  s*    $ 5     ,    J
 \[[["<[[[O < !677; YY//'9


	i 	i 	i 	i , "$A5?A A A 	  
 
  r   )registryrc   r  r  c                     t          |                     dd          |                    d          |                    d                    S )Nr   rD   r8   r   )r   r8   r   )r  rf   )rE   kws     r   rm  rm    sD    |XXfb!!y!!ff_-- /  /  / r   u   🐍i )r  toolsetschemahandlercheck_fnemojimax_result_size_chars)r   )NN)Fro  )>__doc__r   r^   loggingrp   r  r   r  rX   r  rl   r   r   r\   r(  systemr  typingr   r   r   r   	getLoggerr1  rt   r   r'  r"   r%  r&  r3  r  boolr   r$   ru   r2   _COMMON_HELPERSr'   r&   rj   r,  r4  r   r   r   r   r+  r   rM  r  r  ri   r$  r  r#   r  EXECUTE_CODE_SCHEMAtools.registryr  rc   registerr   r   r   <module>r     s   :    				         



       ho9, , , , , , , , , , , , , 
	8	$	$LG+  "	 # # #       D    K+ +^ 38 .  .S	  .,/ .<? .  .  .  .J$P
 
 % R *0 p YXX u<u<u< u< 	u<
 u< u< u< u< u<xa a a a aH3      "s s     K+K+ K+ 	K+
 K+ K+ K+ K+ K+ K+ K+\|2
|2c]|2 DI&|2 		|2 |2 |2 |2J ")-e e
ec]e DI&e 		e e e ePR R R R R R@d      2> >S >D > > > >D 0/11  0 / / / / / / /  	/ / (
!     r   