Introduction
It is tempting to think terminal is the only way to run code. Hermes also ships execute_code, which runs Python in a sandboxed environment with access to Hermes tools. For many tasks, execute_code is a strictly better choice: same flexibility, smaller blast radius, fewer approval prompts. Knowing when to pick which is part of being a fluent operator.
Key Concepts
- execute_code: Runs a Python script in a sandbox. The script can call other Hermes tools (read files, search the web) and combine results.
- terminal: Runs an arbitrary shell command.
- Sandboxing: The boundary that limits what code can reach. execute_code is sandboxed by default; terminal is not (unless you use a container backend).
- Tool composition: Calling multiple tools from within one tool call.
execute_codeis the cleanest way to compose tools when you need imperative logic between calls.
Real World Context
If the agent needs to fetch three URLs, parse JSON from each, and emit a CSV, it has two choices: run three web_extract calls and reason about them in chat, or write a Python snippet via execute_code that does all three in one tool call. The Python option is cheaper (one round trip), more reliable (the logic is explicit), and lower-risk (no shell, no filesystem mutation by default).
Deep Dive
execute_code exists because some workflows are awkward in the conversational loop. Three or more sequential tool calls with logic between them (transform this, then filter by that, then summarize the rest) cost a turn each. Bundling them into one Python snippet is faster and clearer.
The key trade-offs:
| Property | execute_code | terminal |
|---|---|---|
| Language | Python only | Anything the shell can run |
| Sandbox | Yes (Python execution environment) | Depends on backend |
| Access to Hermes tools | Yes, programmatically | No, only what the shell can call |
| Approval prompts | Rarely | Frequently for dangerous patterns |
| Filesystem persistence | Limited to its scope | Full filesystem of the backend |
| Best for | Composing tool calls, data transformation, parsing | Running real commands: build, test, git, install |
A common rule of thumb: if your task is take some data, transform it, return a summary, reach for execute_code. If your task is interact with the system the way a developer would, reach for terminal.
Note that execute_code is not a hiding place. It still runs code, and that code can do real work. But it is a smaller, more predictable surface than a shell.
Common Pitfalls
- Using terminal for what execute_code does better: A long pipeline of
curl | jq | awkis usually clearer as five lines of Python viaexecute_code. - Using execute_code for what terminal does better: Running
pnpm installorgit committhrough Python'ssubprocessdefeats the point. Use terminal for shell-native tasks.
Best Practices
- Prefer execute_code when the work is data-shaped: Parsing, transforming, summarizing data benefits from Python and from being a single call.
- Use terminal for shell-native work: Builds, tests, package management, git. These are not data transformations; they are commands to a system.
Summary
execute_coderuns Python in a sandbox; it can call other Hermes tools.terminalruns arbitrary shell commands and is the largest surface in the registry.- For data transformation and tool composition, prefer
execute_code. - For shell-native developer workflows, prefer
terminal. - The choice changes blast radius, cost, and how often you see approval prompts.
Code Examples
# execute_code task: fetch three URLs, count unique authors
import json
urls = [
'https://api.example.com/posts/1',
'https://api.example.com/posts/2',
'https://api.example.com/posts/3',
]
authors = set()
for url in urls:
page = web_extract(url=url) # Hermes tool, callable from Python
data = json.loads(page['content'])
authors.add(data['author'])
print(f'Unique authors: {len(authors)}')