Skip to main content
A tool is an AI SDK tool an agent may call. defineTool wraps one with Smithers runtime context: a deterministic idempotency key, side-effect metadata, and the side-effect snapshot hook. The five built-in tools (read, write, edit, grep, bash) are themselves defineTool results and run inside the task’s sandbox.
import {
  defineTool,
  getDefinedToolMetadata,
  bash,
  read,
  write,
  edit,
  grep,
  tools,
} from "smithers-orchestrator";

defineTool

Wrap a Zod-validated execute function into a durable AI SDK tool. Pass the result to an agent’s tools or a <Task>’s tools prop.
function defineTool<S extends ZodType>(options: DefineToolOptions<S>): Tool;
name
string
required
Tool name surfaced to the agent and stamped onto ctx.toolName. Also the default description.
description
string
Human-readable description the agent sees. Defaults to name.
schema
ZodType
required
Zod schema for the tool’s input. Validated by the AI SDK before execute runs; execute receives the parsed args.
sideEffect
boolean
default:"false"
Marks the tool as mutating external state. Opts the tool into Smithers side-effect tracking and runs the durability snapshot hook after execute.
idempotent
boolean
default:"!sideEffect"
Whether re-running with the same input is safe. Defaults to true when sideEffect is false. With sideEffect: true, idempotent: false, retries inject a warning that the tool was already called so the agent can verify external state first.
execute
(args, ctx) => Promise<unknown>
required
Runs the tool. args is the parsed input; ctx is the tool context. If sideEffect: true, idempotent: false and execute omits the second ctx parameter, Smithers logs a startup warning, since you almost certainly need ctx.idempotencyKey to handle retries safely.
Tool
object
An AI SDK tool with attached Smithers metadata. Read the metadata with getDefinedToolMetadata.
import { defineTool } from "smithers-orchestrator";
import { z } from "zod";

const placeOrder = defineTool({
  name: "wholefoods.place_order",
  description: "Place a grocery order",
  schema: z.object({ sku: z.string() }),
  sideEffect: true,
  idempotent: false,
  async execute(args, ctx) {
    return wholeFoods.placeOrder({
      sku: args.sku,
      idempotencyKey: ctx.idempotencyKey,
    });
  },
});
The sideEffect / idempotent pair drives retry behavior:
sideEffectidempotentBehavior
falsetruePure read. Replayed freely. No warnings.
truetrueMutates, but replay-safe (e.g. an upsert or PUT). No warnings.
truefalseMutates and not replay-safe (e.g. send email, charge payment). Retry warns the agent and supplies a stable ctx.idempotencyKey.
The rule: if you cannot undo it with git reset, mark it sideEffect: true. Source defineTool.js · context.js · Tests tools-unit.test.js, define-tool-durability.test.js · See also Built-in Tools, SDK agents

getDefinedToolMetadata

Read the Smithers metadata attached by defineTool. Returns null for plain AI SDK tools or non-objects.
function getDefinedToolMetadata(value: unknown):
  | { name: string; sideEffect: boolean; idempotent: boolean }
  | null;
value
unknown
required
A tool (or any value). Inspected for the Symbol.for("smithers.tool.metadata") property.
metadata
object | null
import { getDefinedToolMetadata, write } from "smithers-orchestrator";

getDefinedToolMetadata(write);
// { name: "write", sideEffect: true, idempotent: false }
Source defineTool.js · Tests tools-unit.test.js · See also defineTool

Built-in tools

Five defineTool results, all sandboxed to ctx.rootDir (the workflow directory by default). Paths resolve against the root; symlink escapes are rejected. Output is truncated to ctx.maxOutputBytes (200KB). Import them individually or as the tools bundle:
import { tools } from "smithers-orchestrator";
// tools === { read, write, edit, bash, grep }
const { read, write, edit, bash, grep } = tools;
ToolsideEffectidempotent
readfalsetrue
grepfalsetrue
writetruefalse
edittruefalse
bashtruefalse

read

Read a UTF-8 file from the sandbox.
path
string
required
File path, relative to rootDir or absolute within it.
Returns the file contents. Throws if the file exceeds maxOutputBytes.

write

Write content to a file, creating parent directories as needed.
path
string
required
Destination path, relative to rootDir or absolute within it.
content
string
required
File contents.
Returns "ok". Throws TOOL_CONTENT_TOO_LARGE if content exceeds maxOutputBytes; only the content’s byte size and SHA-256 hash are logged, never the content itself.

edit

Apply a unified-diff patch to an existing file.
path
string
required
File to patch. Must already exist.
patch
string
required
A unified diff. Applied against the current contents.
Returns "ok". Throws TOOL_PATCH_TOO_LARGE (patch over maxOutputBytes) or TOOL_PATCH_FAILED (the patch context does not match the file).

grep

Search for a pattern with ripgrep (rg -n). Requires rg on PATH.
pattern
string
required
Regex to search for.
path
string
default:"."
Directory or file to search, relative to rootDir.
Returns matching lines as file:line:text. No matches returns an empty string; an rg error (exit code 2) throws TOOL_GREP_FAILED.

bash

Run an executable with arguments. There is no shell parsing: pass arguments via args, and for pipes or redirects invoke a shell explicitly, e.g. { cmd: "sh", args: ["-lc", "..."] }.
cmd
string
required
Executable name or path. Up to 8,192 characters.
args
string[]
Arguments. Up to 128 entries, each up to 8,192 characters.
opts
{ cwd?: string }
cwd sets the working directory (sandboxed under rootDir). Defaults to rootDir.
Returns combined stdout and stderr, truncated to maxOutputBytes. A non-zero exit code throws TOOL_COMMAND_FAILED. The process is killed with SIGKILL after ctx.timeoutMs (default 60s; the hard cap is one hour).
Network is blocked unless allowNetwork is enabled (via RunOptions, --allow-network, or server config). When blocked, Smithers rejects commands whose executable basename is curl, wget, npm, bun, or pip, any token beginning with http:// or https://, and git together with a push, pull, fetch, clone, or remote token (throwing TOOL_NETWORK_DISABLED or TOOL_GIT_REMOTE_DISABLED). Local git commands such as git status are allowed. On macOS, a blocked bash additionally runs under sandbox-exec with a network-deny profile when available.
import { ToolLoopAgent as Agent } from "ai";
import { anthropic } from "@ai-sdk/anthropic";
import { createSmithers, tools, Task } from "smithers-orchestrator";
import { z } from "zod";

const codeAgent = new Agent({
  model: anthropic("claude-fable-5"),
  tools, // { read, write, edit, bash, grep }
});

const { Workflow, smithers, outputs } = createSmithers({
  result: z.object({ summary: z.string() }),
});

export default smithers((ctx) => (
  <Workflow name="refactor">
    <Task id="refactor" output={outputs.result} agent={codeAgent}>
      {`Refactor ${ctx.input.file} for readability and run the tests.`}
    </Task>
  </Workflow>
));
Source tools/index.js · bash.js, read.js, write.js, edit.js, grep.js · Tests tools-unit.test.js · See also Built-in Tools, Common External Tools, SDK agents