Mischief

57 / Agent UI

Subagent Tree

Several agents working at once, nested under whoever handed the work down, each with its own state and elapsed time.

Agents3/7 done1 failed

  • Read the issue and split the work, done4.2s

    • Find every call site, done12s

      14 files, 31 calls.
    • Rewrite the call sites, running43s

      • src/upload, done8.1s

      • src/queue, running

      • src/workers, queued

    • Check the generated types, failed6.4s

      Two entries still point at the old signature.
2 of 7 agents running

Installation

Copy the source into your project, or keep it behind a package.

npx shadcn@latest add Tinkerers-Labs/mischief-ui/subagent-tree
import { SubagentTree } from "mischief-ui/subagent-tree"

Or paste it in yourself. The source imports the shared cn helper from @/lib/utils, so point that at your own copy.

registry/default/subagent-tree/subagent-tree.tsx
"use client" import * as React from "react" import { cn } from "@/lib/utils" export type AgentRunStatus = "queued" | "running" | "done" | "failed" export type AgentRun = {  id: string  label: string  status?: AgentRunStatus  /** What it is doing, or what it found. */  detail?: React.ReactNode

Usage

export function Fleet({ runs }) {
  return <SubagentTree runs={runs} />
}

It is nested lists, not a tree widget

The obvious reach here is role=tree with treeitem children and arrow-key navigation. It would be wrong. Nothing in this is expanded, selected or activated: it is work being watched, not a file browser.

A tree widget would announce "tree, level 2, 3 of 4" over that, and take the arrow keys hostage to move between things nobody can do anything to. Nested lists say the same shape, cost nothing, and leave the keyboard alone. Reach for the widget when the nodes become controls.

What a run can be

StatusWhat it means
queuedHanded out, not started
runningWorking now
doneFinished
failedStopped without finishing

Status reaches a screen reader as a word after the label rather than as the colour of a dot, and the running marker stops pulsing when reduced motion is preferred. The count of what is running is announced once for the whole tree, not once per agent, because twelve agents finishing is one piece of news.

API

runsreadonly AgentRun[]The top level agents.
labelstringNames the group. Defaults to Agents.
...rootPropsHTMLAttributes<HTMLDivElement>Native root attributes.

AgentRun

idstringUnique within the tree.
labelstringWhat this agent was asked to do.
status"queued" | "running" | "done" | "failed"Defaults to done.
detailReactNodeWhat it is doing, or what it found.
durationnumberSeconds it has taken.
childrenreadonly AgentRun[]Work it handed down.

Accessibility

Nesting is expressed with nested lists, so a screen reader announces the depth and the number of items at each level without a tree widget's keyboard contract being invented for content that cannot be operated. Each run's status is spoken as a word, never carried by colour alone. One polite live region summarises the whole tree, so a fleet finishing is announced once rather than once per agent. The running marker respects reduced motion.