Sub-agents
When to read this: You want the agent to split a job into parallel or isolated pieces, or you want to drive Chatty from scripts.
A sub-agent is a separate chatty-tui process the parent agent hands a task to, waits on, and reads the answer from. Each child has its own conversation, its own workspace copy, and the same configured models and tools. The parent asks for one through its invoke_agent tool, addressed to local-agent; the child reports its progress back over the local agent broker while it works. That broker hop was measured rather than assumed safe to add: paired against calling the child directly, its median added cost came out at 3 ms or less, well under the variance of a single model turn.
Why bother?
- Parallelism — independent subtasks run at the same time.
- Isolation — a child’s exploration and mistakes stay out of the parent transcript; only its final answer comes back.
- Composition — one agent’s output can feed the next.
- Focus — each child gets one narrow prompt.
Isolated file changes
Sub-agents work in the conversation’s workspace, the folder its folder chip shows, unless their team asks for isolation. A team whose team.json sets "isolate": true (a coding team would; for a roster in module_settings.json it is "team": { "isolate": true }) gives each spawned sub-agent its own git worktree on a new branch (sub-agent/<name>) instead, when the workspace is a git repository with at least one commit. That means two children editing the same file at the same time no longer silently overwrite each other — each keeps its own copy. The branch name is unique per repository: if sub-agent/<name> is already taken (for example, two sub-agent leaders on the same repo naming a worker the same thing), Chatty picks sub-agent/<name>-2, -3, and so on. When a child finishes, its changes are committed to its branch and the worktree is left on disk; the parent agent merges the branch to take the changes. The tool result includes a fenced evidence block naming the actual branch, its commit count, and a diff summary against the base branch — if the project declares a verification command, its exit code and last lines are in there too. A child that committed nothing gets no evidence block, since there’s nothing to merge. If a worktree can’t be created at all (a repository with no commits yet, for example), the delegation fails before the sub-agent starts, rather than silently falling back to a shared tree: you get a red Could not start card saying what failed and what to do, and the agent that asked stops instead of retrying.
Note
If the workspace isn’t a git repository, sub-agents fall back to sharing the parent’s tree as before, so parallel children editing files can still collide.
From the chat
Type /agent <your prompt> to launch a sub-agent inline and watch its progress in the transcript. /agent <name> <prompt> sends the prompt to a named agent instead: one of your agent specs (see Named workers and roles below; a built-in one such as researcher), or a remote agent you have installed as an extension. A name that is neither is just the first word of the prompt. An agent whose spec sets callers is a team member, not an entry point: /agent <member> is refused with a note naming the team to use instead (for example /agent crosscheck-lead), and the picker lists only team leads and standalone agents. In the desktop app, typing /agent (with the space) opens a list of the agents this conversation can reach; type to filter it, and Enter or Tab puts the chosen name in for you.
/agent hands the task to the same local agents the assistant itself delegates to, so the delegation row — and the swarm tree below, when that agent delegates in turn — looks exactly as if the assistant had made the call. If the agent fails or stops before answering, the row ends with the error.
Every local agent is a spec, whoever wrote it. The desktop lists them all on Settings → Agents — name, model, role, the plugins each runs with and what they may do, whether other agents may call it — along with any spec file that doesn’t load and why. In the terminal, /agents prints the same list.
The swarm tree (desktop)
When a sub-agent delegates in turn, its row in the desktop transcript opens into a live tree of agents. Each line shows the agent, its model (or the tool it is running right now), its status and its spend.
- Click a line to open that agent’s transcript, read-only, in a side sheet. The sheet shows a breadcrumb (
lead › writer-0 › drafter-2), the agent’s spec, model, status, turns and spend, its sub-agents and its tool calls. Once a run has finished, the sheet shows its conversation as chat — the prompt, the agent’s text and foldable groups of tool calls; a run still in progress, or one with no captured conversation, lists its tool calls with their arguments and the first lines of each result. A conversation too large to capture says so. The sheet scrolls, as do the footer popovers (agents, MCP, tools, PR checks). Click a sub-agent to go down, or a crumb to go back up. Press Escape or click outside to close it. - Fold an agent with its arrow, or the whole card from its header.
- An agent with many sub-agents shows the first few plus any that are still running or failed, then a +N more done · show all line.
- Stop on a running agent’s line, or Stop this agent in its sheet, stops that one agent and every agent it started. The rest of the swarm keeps running. The agent that called it is told
cancelled_by_userand carries on without it. Any approval or question the stopped agents were waiting on is withdrawn, and the line shows as canceled. - If you stop a turn, runs still drawn as running show as canceled.
Every running agent at once
While any conversation has agents running, the status bar shows a N running chip next to the agents chip (amber, with how many are waiting, when one of them waits on you). Click it to open the running-agents panel: one row per live agent across all your conversations, with its conversation, the agents above it, whether it is working or waiting on your approval or answer, how long it has run, and what it has spent so far. Click a row to jump to that conversation with the agent’s transcript open; Stop on a row stops that agent and every agent it started, like Stop in the tree. The list updates as the agents report; nothing is polled.
A single delegation with no further delegation keeps its plain row. Its ↗ button (on the “Sub-agented” row, once its activity is unfolded) opens that agent’s transcript in the same side sheet; it is greyed out until the agent has started. While an agent is running, its transcript sheet has a message box under the transcript: what you send is read by that agent at its next tool call, not at the end of its run (in the terminal, /msg <agent> <text> does the same). In the terminal, /swarm prints the same tree as text, /agents running prints the running-agents list, and /stop <agent> stops one agent by the name /swarm shows.
Let the agent decide
With tools on (Agents & tools), the parent can ask for children itself when a task splits cleanly:
Task: "Refactor all modules and write tests for each"
→ sub-agent: "Refactor the authentication module and write tests"
→ sub-agent: "Refactor the billing module and write tests"
→ sub-agent: "Refactor the notifications module and write tests"
→ Parent merges the results into a final summary
Note
A child asks before its side effects the way the parent does. When a worker, however deep in the tree, wants to run a command or write a file, the request climbs the chain to your approval card. The card names the agent that asked and its path (
data-coder-0 (root › coordinator › data-coder)). Your answer goes back down to that worker only. See Security & approvals.
Note
Children of one local model server queue rather than run all at once, so a local model is not thrashed by a wide fan-out. The limit is
default_endpoint_budgetin your module settings (hosted APIs such as OpenRouter allow four at a time).
From the terminal
# One headless call
chatty-tui --headless -m "Summarize the changes in the last 5 commits"
# Pipe input
git diff HEAD~3 | chatty-tui --pipe
# Chain: the first agent lists TODOs, the second works through the list
chatty-tui --headless -m "List all TODO comments in src/" | chatty-tui --pipe
--auto-approve skips approval prompts for scripted runs; --enable / --disable pick tool groups per run. Install, modes and keys: Terminal interface.
Add --broker to let that terminal leader spawn sub-agents of its own — without it, only the desktop app can delegate to local-agent:
chatty-tui --headless --broker -m "Refactor the auth module and write tests"
--broker works with --headless, --pipe, and the interactive TUI, and is Unix only.
Named workers and roles
Out of the box the parent can reach local-agent — your default model with the parent’s tools — the built-in agents (researcher, reviewer, writer and the crosscheck-lead / crosscheck-data-lead team leaders), and every spec of your own that doesn’t opt out with exposed = false under [swarm]. A spec that names its callers (a team’s internal members, such as crosscheck-judge) works only for those agents and is never offered to the parent. You can declare your own named workers, each with its own model, a role that limits what it may do, and standing instructions. The parent sees each one as a card — its name, model, role and the first sentence of its instructions — and picks by reading, the same way it would choose a colleague. Nothing else about the parent changes: the prompt and the tool stay identical whether the roster is empty or five deep.
Declare each worker as an agent spec: a small TOML file named after the worker, in your project’s .chatty/agents/ folder (or in Chatty’s data folder under chatty/agents/ to share it across projects — where that is):
# .chatty/agents/local-coder.toml
[agent]
name = "local-coder"
model = "qwen3:14b"
preamble = "You are the coder. Make the smallest change that makes the task's check pass, run it, and report the files you touched."
[tools]
profile = "coder"
[budget]
max_agent_turns = 30
# .chatty/agents/local-reviewer.toml
[agent]
name = "local-reviewer"
model = "gemma3:27b"
preamble = "You are the reviewer. Read the diff, run the tests, and report what you found; never edit the tree."
[tools]
profile = "reviewer"
A spec file is picked up as soon as it exists (on the desktop, Settings → Agents → Reload). To narrow the roster to just some of them, list them by name in module_settings.json, next to your other settings, and restart. This roster is used by the desktop app and by a terminal leader started with --broker:
{
"virtual_agents": ["local-coder", "local-reviewer"],
"team": { "verification": "python3 -m pytest -q" }
}
On the desktop, the enabled field is the Enable module runtime switch in Settings → Plugins. It loads WASM modules; agents do not need it, because the local agent broker runs either way.
A module_settings.json that still describes workers inline (the format before agent specs) no longer loads; move each entry into its own spec file.
| Field | What it does |
|---|---|
agent.name | What the parent addresses; also the branch name, sub-agent/<name>. Must match the file name. |
agent.model | This worker’s model, matched the way chatty-tui --model matches (id, name, or part of the id). Leave it out to use the default model. A worker on a different model server is queued on that server’s budget, not the parent’s. |
tools.profile | A role: coordinator, coder or reviewer. A role is the worker’s whole tool set; anything not in it — including every MCP tool — is gone, so a small model isn’t handed fifty tool schemas before it can read a file. |
agent.preamble | Standing instructions, added to the worker’s system prompt. Its first sentence is what the parent reads on the card, so lead with the role. |
budget.max_agent_turns | How many tool rounds this worker may take before it has to answer. Without it the worker has no turn cap and a 30-minute time budget. This is the worker’s own budget — the parent’s is Max Agent Turns under Settings → Code Execution. |
tools.disable | Tool groups to remove on top of the role (shell, fs-write, git, …). |
A spec in your project folder wins over one of the same name in the data folder, which wins over the built-in ones (the members of the teams below). chatty-tui --agent <name> runs the terminal app as any spec.
The three roles:
| Role | Can | Cannot |
|---|---|---|
coordinator | Read files, search, read git history and diffs, delegate to other workers, merge a worker’s branch, ask you a question. | Edit, run commands, commit. It hands work out; it never does it. |
coder | Everything a coordinator can read, plus write files, run commands, commit on its own branch, run code, query data, remember and save skills. | Delegate further: a coder does not fan out. |
reviewer | Read and search, read diffs (including another worker’s branch), run commands so it can run the tests, query data. | Write, commit, or delegate. It reports; it never fixes. |
A role only ever removes tools: it cannot turn on a tool group you switched off under Settings → Code Execution (or with --disable).
team.verification is one command for the whole roster. When a worker finishes, Chatty commits its branch and then runs that command itself in the worker’s tree — not through the worker — and puts the exit code and last lines into the evidence block the parent reads, next to the branch name, commit count and diff summary. That’s the parent’s proof that the coder’s “tests pass” is true. It is skipped for a worker whose role has no shell, since that worker could not have built anything for it to check.
A worker’s side effects come to you for approval like the leader’s own, whatever its depth (Security & approvals). In an unattended terminal run, --auto-approve lets them through. The full field reference, including how a worker is metered and started, is on the developer page: Named virtual agents.
Teams
--team <id> packages a roster like the one above with a leader and a verification command into one directory, so a run is reproducible and the leader has a role too: a named leader plus co-workers, each with its own model, role and standing instructions, defined once in teams/<id>/team.json, which names the leader’s spec and the workers’ specs. It implies --broker, and for that run the team’s agents replace whatever virtual_agents your module settings declare.
In an unattended run (--headless, --pipe), chatty-tui prints one line to stderr per agent, before the first turn, when that agent’s tool profile (for example coder or reviewer) wants the shell or git but the run has it off (enabled=false, git_enabled=false). It also prints a line when the team’s verification command is set but shell is off. The leader’s --enable, --disable and --only flags are forwarded to every worker, so --enable shell,git gives the whole team a shell and git, and the warnings follow those final settings. These are warnings only; the run goes on unchanged.
Two teams ship built in, both experimental: they are free, run on your own model, and become documented defaults only once a measured result supports them.
| Team | Start it with | What it does |
|---|---|---|
crosscheck (Crosscheck) | chatty-tui --team crosscheck -m "<task>" | Several independent attempts at your task, and a judge picks the strongest; the reply says what it cost. |
crosscheck-data (Crosscheck: Data) | chatty-tui --team crosscheck-data -m "<question about your data>" | The same, with data analysts as the attempts. |
See Crosscheck for when it pays and what it costs. chatty-tui --team <id> runs a team from the terminal. The desktop has no team switch: the team’s leader is on the default roster, so /agent <leader> … runs it, since the leader is an agent like any other. Three standalone agents ship too: researcher, reviewer and writer.
--model runs every agent of the team on that model for the run, including agents whose spec names a model of its own; --tools and --preamble override the leader’s settings. Bring your own team by adding <workspace>/.chatty/teams/<id>/team.json, which overrides both the built-in preset and any team of the same id under your data directory. File format and search order: Teams.
A team can also make what one role hands the next explicit and checkable: team.json’s optional handoffs names a JSON Schema per role, as a path relative to the team directory. A worker running as that role must end its final answer with a fenced json block matching the schema — get it wrong once and Chatty sends the answer back listing the schema errors; get it wrong again and the delegation fails, with the errors visible to the leader. A team with no handoffs behaves exactly as before.
Next
- Tutorial: from one agent to a team: the desktop walkthrough with a data team, approvals, the bill and mixed models
- Tutorial: your first named worker — twenty minutes, one reviewer, real hand-off
- Tutorial: a small agentic team — a coding team directory of your own, with its own playbook
- Tutorial: an autonomous dev team — five agents run unattended in a loop, story by story
- Terminal interface
- Agents & tools