Subagents extension for Tau (a port of pi-subagents).
Project description
tau-subagents
Claude Code-style autonomous subagents for Tau, ported from pi-subagents.
Requires upstream tau-ai ≥ 0.3.0. Upstream tau adopted pi's event and extension protocol wholesale (tau #375), so everything this extension needs — the extension API, the component seam, dialogs, message renderers, the
skills_enabledconfig, provider usage fields — is present in every supported Tau. Older 0.2.x releases (and the pre-0.2.0 fork branches) are no longer supported.
Gives the model an agent tool that delegates a task to a subagent — a
second, scoped Tau coding session running in-process with its own system
prompt, its own tool allow-list, and an in-memory transcript. Foreground
subagents block and return their final report as the tool result. Background
subagents return an id immediately and inject a <task-notification> into the
parent conversation when they finish, which starts a new turn automatically.
Also registers:
get_subagent_result— poll a background agent, orwait=trueto block for itsteer_subagent— inject a message into a running (or queued) agent/agents— interactive menu over agent types, runs, and scheduled jobs- an agents strip under the prompt (TUI mode): live subagent list with an embedded conversation viewer (see "The agents strip")
Install
The implementation lives in src/tau_subagents/; pyproject.toml declares
the entry point via Tau's extension manifest ([tool.tau] extensions = ["src/tau_subagents/extension.py"]), so the repo itself is
loadable as a Tau extension. Either try it per-run:
tau -e /path/to/tau-subagents
or install it permanently by symlinking the clone into Tau's user extensions:
git clone git@github.com:rian-dolphin/tau-subagents.git ~/code/tau-subagents
ln -s ~/code/tau-subagents ~/.tau/extensions/tau-subagents
Update with git pull, then restart tau (or /reload).
Beyond Tau, the extension takes a direct dependency on textual: the agents
strip and conversation viewer are extension-owned Textual widgets (see "The
agents strip").
Use
Ask the model to delegate:
Use a subagent to summarize this repository's architecture.
Spawn an explore subagent to find where slash commands are registered, run it in the background.
/agents manages agents. In the TUI it opens an interactive menu (ported
from pi's showAgentsMenu): selecting a run opens its conversation viewer (see
"The agents strip"), falling back to a dialog submenu (view result / steer /
stop) on hosts without mounted components. pi's create wizard and settings
menu are not ported yet. Headless (print mode) it prints the plain-text list
instead.
The agents strip
The whole agent UI is owned by the extension as real Textual widgets
(src/tau_subagents/ui/), mounted through Tau's generic component seam
(context.ui.components: set_slot_widget / open_main_view /
register_key_interceptor). Tau core stays agent-agnostic — it only hosts
widgets.
In the TUI, spawning a subagent shows a strip under the
prompt (AgentStripWidget) listing main plus every queued/running agent (the
Claude Code pattern). Rows are deliberately quiet: a status glyph (a hollow
circle while running — the spinner/timer/token stats live on the agent
tool-call row in the main chat, not here) plus the agent type and description.
Navigation is focus-free (pi's fleet-list model): the strip never takes
keyboard focus — the prompt keeps it throughout, and a pre-dispatch key
interceptor drives the strip. ← or ↓ at an empty prompt activates nav;
↑/↓ then move the selection, Enter opens the selected agent's
conversation viewer, and Esc (or ↑ past the top, or typing any other key)
deactivates nav and returns the keys to the prompt. Clicking a row opens it
directly.
Enter opens the conversation viewer (ConversationViewer) in the main area
as a display-toggled view — the strip stays visible for peripheral fleet
awareness, and nav stays reachable while viewing: ← re-enters the strip
(↓ scrolls the viewer), so you can switch straight to another agent with
↑/↓ + Enter, or select the main row and press Enter (or click it) to
close the viewer and return to the main transcript. The viewer renders the
run's live conversation (reusing Tau's own transcript rendering), carries a
header with label/status/detail, and embeds a steer composer: Enter opens
it, type + Enter sends a steering message, Esc cancels the composer. x
twice stops the run (a two-press guard), and Esc/q closes the viewer. Live
updates are push-based — the viewer subscribes to the run's change listeners
rather than polling. Finished agents leave the strip after a short linger;
/agents still reaches their transcripts. The agent tool-call row in the main
transcript shows a braille spinner and a live elapsed timer while the run
executes (Tau core behavior, driven by this extension's render_call lines),
plus a cumulative stats ticker (2 turns · 5 tool uses · 41.2k tokens)
streamed through Tau's tool-progress seam. When a foreground run finishes, the
row renders a completion card via the tool's render_result hook — status
glyph, stats line, and a ⎿ result preview (full result and transcript path
under Ctrl+O) — the same card family as background completion notifications,
so foreground and background finishes read alike. Background spawns confirm in
one line (⎿ Running in background (agent-1)).
Esc follows pi's parent-abort semantics: interrupting the main loop while a
foreground subagent runs cancels the child too (the run settles as
∅ cancelled — neutral, not failure-red — and stays reachable via /agents
and get_subagent_result). Background runs are untouched by Esc; they stop
only via the viewer's x x, the /agents menu, or session shutdown.
The UI installs defensively on every session_start (any stale controller is
torn down and fresh widgets mounted), so /new, /resume, and session
rebinds always land exactly one strip.
Agent types
Two built-in types ship with the extension:
general— full coding toolset, for research and multi-step tasksexplore— read-only (read+bash), for searching and summarizing
Add your own as markdown files at .tau/agents/<name>.md (project) or
~/.tau/agents/<name>.md (user). The filename is the type name, the body is
the subagent's system prompt, and frontmatter supports:
---
description: Reviews code for security issues.
tools: read, bash
model: gpt-5.2
---
You are a security reviewer. Investigate the code you are pointed at and
report vulnerabilities with file references.
Project definitions win over user definitions with the same name.
Agent-type frontmatter also supports:
max_turns— non-negative integer soft turn limit (0= unlimited)skills— comma-separated skill names to preload (see below)prompt_mode—replace(default) orappend(see below)memory—user,project, orlocalpersistent memory (see below)isolation—worktreeto run in an isolated git worktree (see below)
Worktree isolation
Pass isolation: "worktree" to the agent tool (or set it in agent-type
frontmatter, which wins) and the subagent runs inside a detached git worktree
of the current repo instead of your working tree. When the run finishes, any
changes are committed (tau-agent: <description>) and preserved on a
tau-agent-<id> branch in the base repo — the result tells you to
git merge tau-agent-<id>. Clean worktrees are removed without a trace.
Isolation is strict: if the cwd is not a committed git repo, the spawn fails
rather than silently running unisolated. Worktree agents cannot be resumed —
their working directory is removed when the run finishes.
Output files
Every run streams its child transcript as JSONL to
<tmpdir>/tau-subagents-<uid>/<encoded-cwd>/<session>/tasks/<agent-id>.jsonl
(first entry is the prompt; new messages are flushed after each turn). The
path is shown in background spawn results (Output file: ...), in completion
notifications (<output-file> tag plus a transcript footer), and in
get_subagent_result output, so you can tail a long-running agent from
outside the conversation.
Per-agent memory (memory: frontmatter)
Set memory: user|project|local on an agent type to give it a persistent
memory directory:
user ~/.tau/agent-memory/<name>/
project <cwd>/.tau/agent-memory/<name>/
local <cwd>/.tau/agent-memory-local/<name>/
At spawn, a memory block is injected into the child's system prompt (before
any preloaded skills) showing the first 200 lines of MEMORY.md and
instructions to keep it as an index linking to detail files. Agents whose
toolset can write (has write or edit) get read-write memory — the
directory is created and read/write/edit tools are ensured; read-only
toolsets get a read-only memory block and no directory is created.
Run records
Every terminal transition (completed, steered, aborted, error, cancelled —
including resumes) appends a compact subagents:record entry to the parent
session log (id, type, description, status, result, error, turns, tool
calls), so subagent history survives session resume. Persistence is
best-effort and never fails a run.
Skills (skills: frontmatter)
Children always inherit Tau's native skill discovery: every subagent session
discovers skills on its own (~/.tau/skills/, ~/.agents/, and the child
cwd's .tau/skills/) and gets an <available_skills> index in its prompt
plus on-demand skill expansion. The skills: frontmatter layers on top of
that:
skills: foo, bar— preloads the named skills' full bodies into the child's system prompt as# Preloaded Skill: <name>sections (a missing name becomes a(Skill "<name>" not found)placeholder), so the child doesn't have to read them on demand. Native discovery is turned off for such agents (matching pi, which setsnoSkillsfor named preloads so the same skill isn't both preloaded and indexed) — the child gets exactly the named skills.skills: true(or*/all) — pins the child's resource discovery (skills and project context files such as AGENTS.md) to the parent working directory. This only makes a difference underisolation: worktree, where default discovery would otherwise resolve against the worktree copy — e.g. uncommitted project skills would be invisible to the isolated child.skills: none/false— disables the child's skill discovery entirely (no<available_skills>index, no/skill:expansion).
In prompt_mode: append the parent prompt prefix already carries the
parent's skill index verbatim.
Model and thinking overrides
The agent tool accepts model (fuzzy or full model selection for the
subagent; default is the agent type's model, else the parent's) and
thinking (one of off, minimal, low, medium, high, xhigh;
default medium). Agent-type frontmatter model: and thinking: win over
the tool params, matching pi's precedence. Note: a typo'd frontmatter
thinking: value is silently ignored (falls back to the param/default),
unlike the tool param, which errors.
prompt_mode: append
By default (replace) the agent body becomes the child's base system prompt.
With prompt_mode: append the child instead keeps the parent session's full
system prompt as a byte-identical prefix, followed by a sub-agent bridge block
(tool-usage etiquette), an <active_agent name="..."/> tag, an environment
block (cwd, git branch, platform), the agent body wrapped in
<agent_instructions>, and any preloaded skill sections. This makes the child
behave like the parent with extra instructions layered on. If the parent
prompt is unavailable, append mode falls back to replace-mode assembly.
Join modes (background notification batching)
defaultJoinMode in settings controls how background completion notifications
are delivered:
smart(default) /group— background agents spawned within a 100ms window form a group; their completion notices are consolidated into one "Background agent group completed" message. If some members are still running 30s after the first finishes, a partial notification is sent ((partial — others still running)) and the stragglers re-group on a 15s cadence until everyone reports.async— never batched; every agent notifies individually (one<task-notification>follow-up each).
Foreground agents never join groups. Members whose results were already read
via get_subagent_result are skipped at delivery time. Groups need at least
two members — a lone background agent always notifies individually.
All completion notifications (individual and group) are held for 200ms before
delivery; reading the result with get_subagent_result inside that window
cancels the now-redundant notification.
Usage reporting
Results and notifications include usage stats: tool uses, tokens, an
estimated context size, and run duration. They appear in the foreground
completion line (Agent completed in <X>s (<N> tool uses, <K> tokens).),
the get_subagent_result header (Usage:), the <usage> block of task
notifications (<total_tokens>), and the steer_subagent confirmation's
Current state: line.
Token figures come in two flavors:
- Real billed tokens (
<total_tokens>,<K> tokens) — from the usage fields onAssistantMessage, populated by Tau's provider adapters. Following pi's semantics, the lifetime total sumsinput + output + cache_writeper assistant response; cache reads are excluded because each turn re-reads the whole cached prefix, so summing them would count the prefix once per turn (pi issue #38). - Context estimate (
~<K> context tokens,<context_tokens>) — Tau's deterministic chars/4 estimate of the child's current context size.
Live activity
While an agent runs, its tool-call row in the transcript animates: a braille
spinner in place of the ▸ marker plus a live elapsed timer (Tau core's
pending-tool rendering). For richer live detail, open the agent's view from
the strip or /agents — per-event textual progress lines were deliberately
removed as transcript noise.
Notification rendering
Background completion notifications render as pi-style cards (status icon,
bold description, dim stats line with turns/tools/tokens/duration, collapsed
result preview, transcript path) instead of raw <task-notification> XML
bubbles — the raw XML still enters the model's context unchanged. Group
notifications stack one card per run.
Concurrency and the background queue
Only background agents count toward a concurrency limit (maxConcurrent,
default 4). Foreground agents always start immediately. When the limit is
reached, further background spawns are queued FIFO — the tool reports the
agent as queued, and the run starts automatically as soon as a slot frees up.
Queued runs show up in /agents with status queued.
Steering a running agent
steer_subagent sends a message to a running agent; it is delivered after the
agent's current tool execution and appears as a user message in the agent's
conversation. Messages sent to a queued agent (one with no live session
yet) are held and flushed the moment its session initializes. Typing inside
an agent's in-place view (see "The agents strip") and the /agents steer
dialog use the same path.
Turn limits (max_turns)
Pass max_turns to the agent tool (or set it in agent-type frontmatter, or
defaultMaxTurns in settings) to cap how many turns an agent runs. When the
limit is hit, the agent is steered with a wrap-up message asking it to give its
final answer. If it keeps going past a grace period (graceTurns, default 5),
it is hard-cancelled. An agent that wraps up within grace finishes with status
steered (treated as success); one that has to be cancelled ends aborted.
Precedence: agent-type frontmatter wins over the tool max_turns param, which
wins over defaultMaxTurns from settings.
Resuming a finished agent
Call the agent tool with resume=<id> and a new prompt to continue a
finished agent's session — its full conversation history is kept alive. Resume
always runs in the foreground and returns the new final answer inline. (Turn
limits are not re-enforced on resume.)
Scheduling agents (schedule)
Pass schedule on the agent tool to run it later or repeatedly, ported
from pi's scheduler. Formats: 5-field cron (0 9 * * 1 — numeric fields
only; *, lists, ranges, */n; minimum granularity one minute), intervals
(5m, 1h; minimum 5s), and one-shots (+10m relative or an ISO
timestamp). Scheduled spawns are always background, bypass the concurrency
queue, and deliver the normal completion notification; schedule is
incompatible with resume, inherit_context, and run_in_background: false, matching pi. Jobs persist per session in
<cwd>/.tau/subagent-schedules/<session_id>.json (PID-locked, atomic);
missed fires are skipped, past one-shots are disabled. Manage jobs via
/agents → Scheduled jobs (list + cancel). Times are naive local time —
across a DST transition a fire can land up to an hour off.
Inheriting the parent conversation (inherit_context)
By default a subagent starts with fresh context. Pass inherit_context: true
on the agent tool (or set it in agent frontmatter; the param wins) to
prepend a digest of the parent conversation to the child's prompt, following
pi's design: user and assistant text turns as [User]: / [Assistant]:
lines (tool results are dropped as too verbose), wrapped in pi's verbatim
# Parent Conversation Context framing. The digest is captured at spawn
time, so queued background runs see the conversation as of the tool call.
Compaction summaries appear as [User] turns (Tau folds them into user
messages during replay) rather than pi's [Summary]: framing.
Settings
Settings are read from two JSON files and shallow-merged, project overriding user (missing or malformed files are ignored):
~/.tau/subagents.json user defaults
<cwd>/.tau/subagents.json project overrides
| key | type | default | meaning |
|---|---|---|---|
maxConcurrent |
int 1–1024 | 4 | max concurrent background agents |
defaultMaxTurns |
int 0–10000 | unlimited | default turn limit (0 = unlimited) |
graceTurns |
int 1–1000 | 5 | extra turns allowed after the soft limit |
defaultJoinMode |
async|group|smart |
smart |
background notification batching (see Join modes) |
Out-of-range or wrong-typed values are silently dropped and the field keeps its default.
Notes and limits
- Subagents run with
extensions_enabled=False, so they cannot spawn subagents recursively. This also means children never receive extension or MCP tools — pi'sisolatedparam (which strips them) has nothing to strip here and is deliberately not ported. - Live activity while a subagent works is the spinner + elapsed timer on its tool row and the in-place view (see "Live activity").
/reloadrebuilds extension state; background runs in flight at reload time are orphaned.
Tests
The tests need Tau's packages importable. The repo's own environment resolves
tau-ai from PyPI:
uv run pytest
Borrowing a Tau checkout's environment also still works:
uv run --project /path/to/tau pytest /path/to/tau-subagents/tests
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file tau_subagents-0.2.0.tar.gz.
File metadata
- Download URL: tau_subagents-0.2.0.tar.gz
- Upload date:
- Size: 61.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.31 {"installer":{"name":"uv","version":"0.11.31","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
acc90901c3ea03418d1b8977385faa8e8f84c0a4b0d2e25f4d6f4c68e730ab13
|
|
| MD5 |
514fd3723d31fb11daa740420b9e0ad9
|
|
| BLAKE2b-256 |
061ab21bbb644b39b7a98a3964d71d21277f7a774d0884a8ead517114a79b112
|
File details
Details for the file tau_subagents-0.2.0-py3-none-any.whl.
File metadata
- Download URL: tau_subagents-0.2.0-py3-none-any.whl
- Upload date:
- Size: 70.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.31 {"installer":{"name":"uv","version":"0.11.31","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2c45f09fb3862e131070e0915a411287f8da58dee2789a72f23e11cb280e8b67
|
|
| MD5 |
ac0ef6462604f00281443643b9900200
|
|
| BLAKE2b-256 |
58e7fcc7099690276f81190c6ce538728092cbab849077e87443549883a42203
|