Tool System
Available tools, execution policy, and safety guards.
The agent uses a tool-calling interface to execute shell commands on the host system. Tool definitions and handlers live in internal/agent/tools.go (~435 LOC).
Available Tools
| Tool Name | Alias | Description |
|---|---|---|
bash | run_command | Execute a shell command via bash -lc |
Both names map to the same handler. The tool schema requires a description field: the model must state its intent for every call.
Execution Flow
- Agent runtime extracts tool name and arguments from the LLM response
- Policy check (
internal/agent/policy.go): validates the command is non-empty - Runner (
internal/executor/runner.go): executes viabash -lc - Output is captured, truncated to 64KB, and wrapped in an observation envelope
- Observation is appended to chat history for the next LLM turn
Command Policy
The policy is pure pass-through (internal/agent/policy.go, 19 LOC):
- No hardcoded deny-list
- No allowlist
- No sandboxing, chroot, namespace isolation, or seccomp
- Only empty commands are blocked
The system prompt guides behavior. Code does not second-guess commands.
Interactive Command Blocking
A heuristic in tools.go (looksInteractiveCommand) blocks bare interactive commands that would hang the agent on a TTY prompt:
ssh host,mysql,psql,mongo/mongosh,sqlite3,sftp,ftp,telnet,passwd- Any
read -por--askpass
These are allowed when batch-mode flags are present (-e, -c, --eval, <<, <, or a remote command after the host). This is a UX guard to prevent the agent from hanging, not a security isolation control.
Runner Details
// internal/executor/runner.go
func Run(ctx context.Context, command string, timeout time.Duration) Result- Shell:
bash -lc '<command>' - Default timeout: 60 seconds
- Maximum timeout: 10 minutes
- Output capture: stdout + stderr combined
- Output truncation: 64KB max (truncation marked in observation)
- Exit code captured in result; 124 on timeout
Observation Schema
Tool results are returned as a structured JSON envelope (schema: z2e.tool_observation.v1):
{
"schema": "z2e.tool_observation.v1",
"timestamp": "2026-07-28T15:48:00Z",
"session": {
"started_at": "...",
"steps": 5,
"executed_commands": 3,
"observations": 3
},
"tool": {
"tool": "bash",
"call_id": "call_xxx",
"command": "nmap -sV example.com",
"workdir": "/home/user",
"raw": "compact(2000 chars)"
},
"result": {
"status": "completed",
"exit_code": 0,
"timed_out": false,
"interactive": false,
"output": "compact(6000 chars)"
}
}The output field is compacted to 6000 characters for the LLM prompt. The raw capture buffer is 64KB.
Safety Guards
Doom-Loop Detection
3 consecutive identical command signatures (name + raw arguments) cause the loop to skip that call and warn the model.
Output Overflow
Output beyond 64KB in the raw capture is truncated. The LLM-facing output field is further compacted to 6000 characters.
Timeout Enforcement
Commands exceeding the timeout are killed via context cancellation. The observation includes the partial output captured before termination, with exit code 124.
Empty Command Rejection
Empty or whitespace-only commands are rejected at the policy level.