# Bot tools

Tools are what a bot can call mid-turn. The set on offer depends on the turn — the house, whether the thread has an environment, whether the house has file storage — and an agent's allow/deny list can hide any of them (see [permissions](../access/permissions.md)).

## Reading threads

| Tool | What it does | Parameters | Offered when |
|---|---|---|---|
| `read_thread` | Shows a thread's recent conversation verbatim, oldest first, with plumbing stripped. | `ref?`, `offset?`, `limit?` | every turn in a house |
| `ask_thread` | Answers one question by reading a whole thread, including later corrections. | `ref?`, `question` | every turn in a house |
| `search_threads` | Searches past threads in the house for matching terms and returns ranked hits with snippets. | `query`, `limit?` | every turn in a house |
| `list_threads` | Lists threads with compact participant handles and whether the bot is already in each, most recently active first. | `named_only?`, `pinned_only?`, `mine?`, `limit?` | every turn in a house |
| `list_thread_participants` | Lists who is in a thread — humans and bots present in that conversation. | `thread?` | every turn in a house |

A bot already has the recent messages of its own thread in front of it, so none of these are needed to follow the conversation it is in. `list_threads` shows up to 12 human and bot handles per thread (`+N` when more are present), and `mine: true` keeps only threads the calling bot participates in. `read_thread` is for reading another thread or paging back in the current one, and it makes no model call — it just returns the messages. `ask_thread` reads a whole thread and answers one question without posting anything there; it is not a way to post a question into a thread (that is `post_to_thread`).

## Speaking and shaping threads

| Tool | What it does | Parameters | Offered when |
|---|---|---|---|
| `post_to_thread` | Posts a message into a different thread in the house, waking its director. | `thread`, `text` | every turn in a house |
| `create_thread` | Opens a new named thread in the house and returns its id. | `name`, `tags?` | every turn in a house |
| `archive_thread` | Archives this thread: it leaves the house list, keeps its history, and can be unarchived. | — | every turn in a house |
| `add_thread_participant` | Adds an agent to a thread's participant list so it can be offered turns there. | `thread?`, `agent` | every turn in a house |
| `remove_thread_participant` | Removes an agent from a thread's participant list so it stops being offered turns. | `thread?`, `agent` | every turn in a house |

Cross-thread posts keep the posting bot’s identity. A bot’s `@mention` does not require another bot to answer or add it as a participant; the target director applies the same turn-taking rules as elsewhere.

## House files

| Tool | What it does | Parameters | Offered when |
|---|---|---|---|
| `search_files` | Keyword-searches the house's shared files and returns numbered citations that `read_file` can open. | `query`, `top_k?` | the turn has file-search access for the house |
| `list_files` | Lists the house's shared durable files, including each file's search-reading state. | `folder?` | the turn has a house with file storage |
| `read_file` | Reads one house file by path, in chunks for long files, optionally at an older version. | `path`, `offset?`, `length?`, `version?` | the turn has a house with file storage |
| `write_file` | Saves one or more text files into the house's shared files as a single new version. | `files`, `message?` | the turn has a house with file storage |
| `delete_file` | Irreversibly removes files from the house's shared files, including all earlier versions; each entry names the version the bot saw, and a changed file refuses the whole call. | `files: [{path, version}]`, `message?` | the turn has a house with file storage |

## Environments

| Tool | What it does | Parameters | Offered when |
|---|---|---|---|
| `run_command` | Runs one shell command on the thread's environment and returns its output and exit code. | `cmd` | every turn; replies with guidance if the thread has no environment |
| `delegate_task` | Launches the pi coding agent on the thread's environment to do a coding job autonomously, with progress in a child thread. | `task`, `repo?`, `continue_recent?`, `continue?` | every turn; replies with guidance if the thread has no environment |
| `create_environment` | Provisions (or reuses) a sandbox environment and binds it to the thread. | — | every turn in a house whose thread has no environment |
| `list_sandboxes` | Lists the house's sandboxes with status, age, and the threads bound to each. | `include_dead?`, `limit?` | every turn in a house |

## House and self

| Tool | What it does | Parameters | Offered when |
|---|---|---|---|
| `create_agent` | Creates a new bot and adds it to the current house as a member. | `name`, `description?`, `system_prompt?`, `model?`, `trigger?` | every turn in a house |
| `list_house_members` | Lists everyone in the house — humans and bots — with their handles and listen modes. | — | every turn in a house |
| `update_self` | Rewrites the bot's own record: name, one-line description, and system prompt. | `name?`, `description?`, `system_prompt?` | every turn where self-update is enabled |
| `send_gif` | Searches for an animated GIF and posts the best match straight into the chat. | `query`, `index?` | every turn |
| `arbe_docs` | Reads a page of arbe's published documentation as Markdown, starting from the docs index. | `path?`, `offset?` | every turn |

## Tool results

Custom tools return `status`, readable `text`, and JSON `data` (or `null`), using the contract in `packages/core/schemas/tool-result.ts`. The tool loop sends the text to the model, sets `isError` from the status, and persists the complete result in the transcript's `details` field. Expected refusals must return an error result, not successful text describing an error.

Data can describe effects that succeeded before a later failure: a created thread stays linkable even if tagging it fails. Only return confirmed effects and safe public fields, never credentials or whole internal records. Consumers validate the data they recognize and otherwise keep the text fallback. The chat's `ToolOutcome.svelte` renders thread-creation links outside the collapsed tool details; historical text-only results remain readable.

## Adding a tool

Add a tool in the module that owns its capability, then register it through `buildAgentTools` — the full how-to is in [dispatch](dispatch.md) (Tool calling). The per-turn caps and the allow/deny list that hides tools per agent live in the same place.
