Tools
Nexo attaches four sandbox-backed tools — ReadFile, WriteFile, Shell, and Glob — each gated by the sandbox and permission seams. The web tools (fetch and search) live in the Web guide.
Nexo attaches four sandbox-backed tools — ReadFile, WriteFile, Shell, and Glob — each gated by the sandbox and permission seams. Which tools attach depends on what the sandbox supports; what they may do depends on the permission mode.
The four sandbox tools
| Tool | Capability | What it does | Attached when |
|---|---|---|---|
ReadFile |
:read |
Read a file from the sandbox | Always |
WriteFile |
:write |
Write a file into the sandbox | Always (gated by the permission mode) |
Glob |
:glob |
Match files by pattern | Always |
Shell |
:shell |
Run a shell command in the sandbox | Only when sandbox.supports?(:shell) |
ReadFile/WriteFile/Glob are always attached. Shell attaches only when the sandbox supports it — so a :virtual agent never advertises a Shell tool it can never run. Local/Container support all four capabilities; Virtual supports everything but :shell (it raises NotImplementedError on purpose — in-memory means no command execution).
Safe by default: under
:read_only,:read/:globare auto-allowed and:write/:shellare denied — the agent can look but not touch. Grant individual capabilities withPermissions.new(mode: :read_only, allow: %i[read glob fetch])without changing the mode.
Shell — output truncation
Unbounded command output (npm install, git log) is truncated before it reaches the model, so a single command can’t blow a small context window. Tools::Shell wraps stdout/stderr through Nexo::OutputTruncator.call(text, max_lines: 200, max_chars: 16_000) — strips ANSI escapes, keeps the last max_lines lines, appends a …[truncated N lines] marker, then caps at max_chars. The integer status passes through untouched.
Pure line/char truncation — no tokenizer; configurable via the kwargs only (no global config, no per-agent macro).
WriteFile — read-before-write + stale guard
Within a session, the agent is blocked from overwriting a file it never read, or one that changed underneath it. Agent#chat builds one Nexo::ReadTracker per chat and threads it into ReadFile (records (path, mtime) on a successful read) and WriteFile (enforces):
- Overwriting an existing, un-read file returns
{error: "read <path> before overwriting it"}. - A file whose mtime changed since the read returns
{error: "stale: <path> changed since you read it"}. - A new file writes freely.
The guard is real-FS only — skipped entirely on Virtual (nil mtime) and when no tracker is passed (direct tool construction). Best-effort: mtime-based, so a sub-second external edit may slip past the stale check (read-before-write is the primary guard). Clobber-safety within a session only — no versioning, locking, or VCS semantics.
Failure model — errors, not exceptions
A denied capability returns { error: ... } to the model (recoverable) and never raises into the loop — identical to a sandbox tool failure. A path that escapes the workspace raises SecurityError (sandbox misuse); everything else surfaces as recoverable context for the model to adapt to.
Web tools — fetch and search
The fetch tool for reading the web and the search tool for discovering URLs live in the Web guide. They are gated by their own :fetch and :search capabilities (denied under :read_only exactly like :shell) plus a host allow-list / an injected backend, and they run in the host process — no sandbox constrains them, not even a --network none container.
These are sandbox refinements as much as tool behavior — see Sandboxes for the guard details behind each capability, and MCP for attaching external tool servers through the protocol.