Skip to main content

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/:glob are auto-allowed and :write/:shell are denied — the agent can look but not touch. Grant individual capabilities with Permissions.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.


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.


Next steps