tools bundles all five tools keyed by name:
Sandboxing
All tools are sandboxed torootDir (defaults to the workflow directory). Paths are resolved relative to this root; escapes via symlinks are rejected.
| Policy | Behavior |
|---|---|
| Path resolution | Relative paths resolve against rootDir. Absolute paths must fall within root. |
| Symlinks | Rejected if target is outside sandbox. |
| Output size | Truncated to maxOutputBytes (default 200KB). |
| Timeouts | bash and grep default to 60s; exceeded processes killed with SIGKILL. |
| Network | bash blocks network commands by default. See bash. |
Tool Call Logging
Every invocation is logged to_smithers_tool_calls:
| Field | Description |
|---|---|
runId | Workflow run ID |
nodeId | Task node that invoked the tool |
iteration | Loop iteration |
attempt | Retry attempt number |
seq | Sequential call counter within the task |
toolName | read, write, edit, grep, or bash |
inputJson | Serialized input arguments |
outputJson | Serialized output (truncated if over limit) |
startedAtMs | Start timestamp |
finishedAtMs | End timestamp |
status | "success" or "error" |
errorJson | Error details (if "error") |
defineTool
UsedefineTool() to wrap custom AI SDK tools with Smithers runtime context, deterministic idempotency keys, and durable tool-call logging.
ctx.idempotencyKeyis stable across retries and resumes for the same task iteration.sideEffect: trueopts the tool into Smithers side-effect tracking.idempotent: falsetells Smithers to warn resumed/retried agent loops when the tool was already called in a previous attempt.- Smithers logs start/finish records for every
defineTool()call in_smithers_tool_calls.
Side Effects and Idempotency
Every custom tool that modifies external state must declaresideEffect: true. This is how Smithers knows to protect your workflow during retries and resumes. Without it, Smithers treats the tool as a pure read and will replay it freely — potentially sending duplicate emails, double-charging payments, or creating duplicate records.
The two flags work together:
sideEffect | idempotent | Smithers behavior |
|---|---|---|
false (default) | true (default) | Pure read. Safe to replay on retry. No warnings. |
true | true | Mutates external state, but calling it twice with the same input produces the same result (e.g. an upsert, a PUT request). Safe to replay. No warnings. |
true | false | Mutates external state and is not safe to replay (e.g. sending an email, placing an order, charging a payment). On retry, Smithers injects a warning telling the agent the tool was already called and it should verify external state before calling it again. |
sideEffect: true and idempotent: false, Smithers does two things on retry:
- Warns the agent. The retry prompt includes a message listing which non-idempotent tools were already called, so the agent can check external state before repeating them.
- Provides a stable idempotency key.
ctx.idempotencyKeyis deterministic for a given task + iteration, so you can pass it to external APIs that support idempotency (Stripe, AWS, etc.) to deduplicate on their end.
execute function has sideEffect: true, idempotent: false but does not accept the ctx parameter, Smithers logs a warning at startup. This is almost always a bug — you need ctx.idempotencyKey to safely handle retries.
What counts as a side effect
A side effect is any mutation of state outside the sandbox. If the tool talks to an external API, writes to a database, sends a message, or triggers a webhook, it has a side effect. Mark it. File system changes inside the sandbox — writing files, editing code, runninggit commit — are not side effects in this sense. The built-in write, edit, and bash tools modify the working directory, but those changes are local, sandboxed, and tracked by git. They are inherently reversible (git checkout, git reset) and inspectable (git diff, git log). Smithers does not need retry warnings or idempotency keys for them.
| Tool | Side effect? | Why |
|---|---|---|
Built-in read, grep | No | Pure reads |
Built-in write, edit | No | Sandboxed file changes, tracked by git |
Built-in bash (local commands) | No | Local execution within sandbox |
| Custom tool calling an external API | Yes | Mutates state outside the sandbox |
| Custom tool writing to a database | Yes | External persistent state |
| Custom tool sending a Slack message | Yes | Irreversible external communication |
| Custom tool creating a GitHub PR | Yes | External state visible to others |
git reset, mark it as a side effect.
read
Read a file from the sandbox."File too large" if size exceeds maxOutputBytes.
write
Write content to a file. Creates parent directories as needed."ok". Throws "Content too large" if content exceeds maxOutputBytes. Logs content hash (SHA-256) and byte size; full content is not stored.
edit
Apply a unified diff patch to an existing file."ok". The file must exist. Reads current contents, applies the patch via applyPatch, writes back. Throws on size limits ("Patch too large", "File too large") or mismatched context ("Failed to apply patch"). Logs patch hash and byte size.
grep
Search for a regex pattern usingripgrep.
rg -n format). Exit code 1 (no matches) returns empty string. Exit code 2 throws stderr as error. Requires ripgrep in PATH.
bash
Execute a shell command.rootDir. Timeout: 60s (killed with SIGKILL via process group). Non-zero exit codes throw.
Network Blocking
Controlled byallowNetwork in RunOptions, --allow-network on CLI, or server config. Default: blocked.
When blocked, the command string (executable + args) is checked against these fragments:
| Category | Blocked strings |
|---|---|
| HTTP clients | curl, wget |
| URL prefixes | http://, https:// |
| Package managers | npm, bun, pip |
| Git remote ops | git push, git pull, git fetch, git clone, git remote |
git status, git diff, git log) are allowed.
Using Tools with Agents
Pass tools to an AI SDK agent, assign the agent to a<Task>:
Configuration
| Option | Default | Description |
|---|---|---|
rootDir | Workflow directory | Sandbox root |
allowNetwork | false | Allow network commands in bash |
maxOutputBytes | 200000 (200KB) | Max output size per tool |
toolTimeoutMs | 60000 (60s) | Timeout for bash and grep |