Docs
Built-in Tools
Both adapters ship the same toolset over their filesystem, so the agent behaves identically whether the files live on disk or in IndexedDB. Add your own tools with the tools option; connect whole toolboxes over MCP.
The default set#
| Tool | What it does |
|---|---|
| Read | Read a file (with offsets for large files). |
| Write | Create or overwrite a file. |
| Edit | Exact string replacement within a file. |
| MultiEdit | Several edits to one file in a single atomic call. |
| Delete | Remove a file. |
| Glob | Find files by pattern. |
| Grep | Search file contents by regex. On Node it uses real ripgrep when available and falls back to an ignore-aware JS search — see the Node docs. |
| LS | List a directory. |
| TodoWrite | Maintain a task list across a long turn. |
| WebFetch | Fetch a URL and read it as text. |
| Bash | Run shell commands — sandboxed by default (see below). |
The virtual shell#
Bash runs in an in-memory sandbox over the agent's filesystem — real pipes, redirection, globbing and dozens of coreutils, with no child processes and no host access. On Node it can escalate to the host shell with host: true, which always requires approval.
@nanocodana/core and @nanocodana/browser ship a Node-free build of the shell, so they load unmodified on Cloudflare Workers and in strict bundlers — no polyfills, no configuration. The trade is that the wasm- and native-backed commands are unavailable there: sqlite3, python3, js-exec and tar throw. Everything else works normally, including gzip, gunzip, zcat and rg -z, which the Node-free build implements without node:zlib. @nanocodana/nodejs uses the full shell and has all of them.
Pass an object instead of a boolean to configure it. On the Node adapter, { python: true } and { javascript: true } switch on python3 and js-exec:
new NanoCodana({
model,
virtualBash: { env: { CI: '1' }, maxCommandCount: 500 },
})Set virtualBash: false to drop the tool entirely. That stops a code-splitting bundler (webpack, Vite, Next.js) from ever fetching the shell chunk — but it does not shrink a single-file build, because no bundler can eliminate a reachable dynamic import based on a runtime flag. To remove the bytes rather than defer them, import @nanocodana/core/no-bash — the same API without the bundled shell. On a Cloudflare Worker that is 854 KiB → 448 KiB gzipped.
Adding your own tools#
Pass a record of AI SDK tools (or a (ctx) => record factory) via tools — they merge with the built-ins and obey the same approval rules:
import { tool } from 'ai'
import { z } from 'zod'
const agent = NodeAgent({
model,
workingDirectory: process.cwd(),
tools: {
DeployPreview: tool({
description: 'Deploy the current build to a preview URL',
inputSchema: z.object({ note: z.string() }),
execute: async ({ note }) => deployPreview(note),
}),
},
})disableTools: ['Bash'] or run a read-only turn with activeTools: ['Read', 'Grep'] — see Per-call Overrides.