Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Tool System

Zeph provides a typed tool system that gives the LLM structured access to file operations, shell commands, and web scraping. Each executor owns its tool definitions with schemas derived from Rust structs via schemars, ensuring a single source of truth between deserialization and prompt generation.

Tool Registry

Each tool executor declares its definitions via tool_definitions(). On every LLM turn the agent collects all definitions into a ToolRegistry and renders them into the system prompt as a <tools> catalog. Tool parameter schemas are auto-generated from Rust structs using #[derive(JsonSchema)] from the schemars crate.

Tool IDDescriptionInvocationRequired ParametersOptional Parameters
bashExecute a shell command```bashcommand (string)
readRead file contentsToolCallpath (string)offset (integer), limit (integer)
editReplace a string in a fileToolCallpath (string), old_string (string), new_string (string)
writeWrite content to a fileToolCallpath (string), content (string)
globFind files matching a glob patternToolCallpattern (string)
grepSearch file contents with regexToolCallpattern (string)path (string), case_sensitive (boolean)
web_scrapeScrape data from a web page via CSS selectors```scrapeurl (string), select (string)extract (string), limit (integer)

FileExecutor

FileExecutor handles the file-oriented tools (read, write, edit, glob, grep) in a sandboxed environment. All file paths are validated against an allowlist before any I/O operation.

  • If allowed_paths is empty, the sandbox defaults to the current working directory.
  • Paths are resolved via ancestor-walk canonicalization to prevent traversal attacks on non-existing paths.
  • glob results are filtered post-match to exclude files outside the sandbox.
  • grep validates the search directory before scanning.

See Security for details on the path validation mechanism.

Dual-Mode Execution

The agent loop supports two tool invocation modes, distinguished by InvocationHint on each ToolDef:

  1. Fenced block (InvocationHint::FencedBlock("bash") / FencedBlock("scrape")) — the LLM emits a fenced code block with the specified tag. ShellExecutor handles ```bash blocks, WebScrapeExecutor handles ```scrape blocks containing JSON with CSS selectors.
  2. Structured tool call (InvocationHint::ToolCall) — the LLM emits a ToolCall with tool_id and typed params. CompositeExecutor routes the call to FileExecutor for file tools.

Both modes coexist in the same iteration. The system prompt includes invocation instructions per tool so the LLM knows exactly which format to use.

Iteration Control

The agent loop iterates tool execution until the LLM produces a response with no tool invocations, or one of the safety limits is hit.

Iteration cap

Controlled by max_tool_iterations (default: 10). The previous hardcoded limit of 3 is replaced by this configurable value.

[agent]
max_tool_iterations = 10

Environment variable: ZEPH_AGENT_MAX_TOOL_ITERATIONS.

Doom-loop detection

If 3 consecutive tool iterations produce identical output strings, the loop breaks and the agent notifies the user. This prevents infinite loops where the LLM repeatedly issues the same failing command.

Context budget check

At the start of each iteration, the agent estimates total token usage. If usage exceeds 80% of the configured context_budget_tokens, the loop stops to avoid exceeding the model’s context window.

Permissions

The [tools.permissions] section defines pattern-based access control per tool. Each tool ID maps to an ordered array of rules. Rules use glob patterns matched case-insensitively against the tool input (command string for bash, file path for file tools). First matching rule wins; if no rule matches, the default action is Ask.

Three actions are available:

ActionBehavior
allowExecute silently without confirmation
askPrompt the user for confirmation before execution
denyBlock execution; denied tools are hidden from the LLM system prompt
[tools.permissions.bash]
[[tools.permissions.bash]]
pattern = "*sudo*"
action = "deny"

[[tools.permissions.bash]]
pattern = "cargo *"
action = "allow"

[[tools.permissions.bash]]
pattern = "*"
action = "ask"

When [tools.permissions] is absent, legacy blocked_commands and confirm_patterns from [tools.shell] are automatically converted to equivalent permission rules (deny and ask respectively).

Output Overflow

Tool output exceeding 30 000 characters is truncated (head + tail split) before being sent to the LLM. The full untruncated output is saved to ~/.zeph/data/tool-output/{uuid}.txt, and the truncated message includes the file path so the LLM can read the complete output if needed.

Stale overflow files older than 24 hours are cleaned up automatically on startup.

Configuration

[agent]
max_tool_iterations = 10   # Max tool loop iterations (default: 10)

[tools]
enabled = true
summarize_output = false

[tools.shell]
timeout = 30
allowed_paths = []         # Sandbox directories (empty = cwd only)

[tools.file]
allowed_paths = []         # Sandbox directories for file tools (empty = cwd only)

# Pattern-based permissions (optional; overrides legacy blocked_commands/confirm_patterns)
# [tools.permissions.bash]
# [[tools.permissions.bash]]
# pattern = "cargo *"
# action = "allow"

The tools.file.allowed_paths setting controls which directories FileExecutor can access for read, write, edit, glob, and grep operations. Shell and file sandboxes are configured independently.

VariableDescription
ZEPH_AGENT_MAX_TOOL_ITERATIONSMax tool loop iterations (default: 10)