Skip to content

mecatui: subagent / Task visibility (agent hierarchy dashboard) #1

Description

@JAORMX

Summary

mecatui has no visibility into subagent / Task activity. This is the largest remaining gap from the mecatui UX review (it mirrors the loudest emergent 2026 community ask — the "agent hierarchy dashboard" pattern). mecatl spawns subagents two ways and both are a black box in the TUI today:

  1. The Task tool (internal/agent/subagent.go) runs a fresh read-only agent in an isolated context and, by design, drains the child's entire event stream inside Execute (drainChild) — discarding every turn.start / tool.call / tool.result / message.delta and folding back only the final summary string. The user sees a single Task tool card spin on … then resolve. No goal, no progress, no per-child tokens, no elapsed.
  2. Agent teams (internal/team/, internal/adapter/server/team.go, the RunTeam stream in harness.proto) drive N long-lived member sessions. Here the data already exists: RunTeam streams a TeamEvent{member, Event} per member. But the TUI has no concept of a member, so N interleaved streams would collapse into one unreadable scrollback.

Design principle: one model, 1→N

The TUI renders purely from streamed events. So the design reduces to: give every event a parent/member identity, route by identity, render an Agents panel keyed by identity. A single Task subagent is the N=1 case of a team roster. Reuse three existing idioms: the MCP overlay pattern (opt-in, esc-dismissable, ctrl+* to open), the ctrl+t expand toggle, and the Tier-A context meter (▒▓█░ per-band glyph bar) for per-agent context %. The single-agent conversation view stays untouched; subagent activity never pollutes the main scrollback unless the user opts in.

1. Single Task subagent — inline card (MVP)

Keep the existing blockTool card as the anchor; while unresolved, show a live sub-status line fed by new lightweight events.

Collapsed (default):

 … Task  "audit auth middleware for missing checks"
   ⟳ subagent · Grep · 8.2s · ↑3.1K ↓410 · 3 tools · ctrl+t details

Expanded (ctrl+t) — a capped, opt-in trace of the child's tool calls (the only place the child's interior is shown; tool names/status only, never child message text):

 … Task  "audit auth middleware for missing checks"
   subagent subagent-call_7f3 · running · 8.2s · ↑3.1K ↓410
     ✓ Grep   "func.*Handler" internal/http      12 matches
     ✓ Read   internal/http/mw.go                 1–80
     ⟳ Grep   "ValidateToken"
   … child trace capped at 12 calls · ctrl+t collapse

On completion the card resolves as today (summary string → resultBody), with a terminal stat line:

 ✓ Task  "audit auth middleware for missing checks"
   subagent done · 14.6s · ↑5.2K ↓820 · 6 tools · 0 writes
   Found 2 handlers that call the DB before ValidateToken… (summary)

This MVP needs no new panel/key — just intermediate events and one status line on the existing card.

2. Multi-agent — the Agents panel (ctrl+a)

A new overlay styled like the MCP inventory panel. Each row is one identity (lead at the root, members beneath): state · current activity · tokens · context meter.

┌─ agents · team-3kf9 · 4 members · round 6 ──────────────────────┐
│                                                                  │
│  ◆ lead       working   delegating                              │
│     ↑12K ↓2.1K · ctx ▓▓▓▓▒░░░ 58% · 92K/200K                    │
│                                                                  │
│  ├─ ◆ scout    working   Grep "ValidateToken"   6.1s            │
│  │     ↑8.4K ↓900  · ctx ▒▒▒░░░░░ 31% · 62K/200K               │
│  │                                                               │
│  ├─ ◆ patcher  working   Edit internal/http/mw.go  2.3s         │
│  │     ↑31K ↓7.2K · ctx █████▓▓░ 88% ⚠ · 176K/200K              │
│  │                                                               │
│  └─ ○ scribe   idle      awaiting message                       │
│        ↑2.1K ↓310  · ctx ▒░░░░░░░ 9%  · 18K/200K                │
│                                                                  │
│  tasks  5 done · 1 in-progress · 2 pending(blocked)             │
│  ↑/↓ select · enter focus member · t tasks · esc close          │
└──────────────────────────────────────────────────────────────────┘
  • ◆ working/spawning, ○ idle, dim ✓ stopped — glyph-encoded state (colourblind-safe, survives ANSI stripping), mirroring the context-meter glyph-not-colour rule.
  • patcher at 88% ⚠ reuses the context meter's existing danger mark — directly answering "which agents are near their context limit."
  • Tree connectors express parent→member; the lead sits at the root (it is a team.Member with the Lead flag).
  • enter focuses a member → its tagged events render in the main viewport (a per-member filter over the buffered TeamEvent stream), so one agent's transcript is readable without the N-way interleave. esc returns.
  • t flips to the shared task list (ListTeam already returns team.Task: id · state · assignee · deps).
  • A footer summary advertises activity without forcing the panel open: ⟳ team-3kf9 · 3/4 working · ctrl+a agents.

Auto-open policy: do not auto-open (it steals the keyboard, like the idle-only MCP overlay). Surface the footer summary when a team is live; ctrl+a opens the panel. The single-Task case never opens a panel — its inline card is enough.

3. Data / events the harness must emit

Surface Today Needed
Team members RunTeam already streams TeamEvent{member, Event}; TeamMember{name, agent_type, state} and TeamTask exist. TUI client consumes the RunTeam stream and routes by member into per-identity render state. The session Event already carries usage/turn_end (tokens). Gap: per-member context-window size isn't on TeamMember — add context_used/context_window (or surface the member session's cumulative Usage + model window) so the per-agent meter has a denominator. The member's current tool is derivable client-side from the latest tool.call per member (likely no new field).
Task subagent subagent.go's drainChild emits nothing intermediate — the parent sees one tool.result. Add minimal child-progress events the parent forwards instead of discarding, carrying parent_call_id + child session_id: subagent.start{goal, child_id}, subagent.tool{child_id, tool_name, ok}, subagent.end{child_id, usage, tool_count, stop}. handleChildEvent is the single chokepoint — it can forward a redacted projection (tool names + usage, not child message text, preserving context-isolation) up to the parent's EventSink.

Both cases converge on the same client-side shape: (identity, kind, activity, usage, ctx). Team tags by member; Task tags by child session_id. For N=1 (Task) the "roster" is the inline card; for N>1 (team) it's the panel.

4. Open questions & tradeoffs

  • Screen real estate — the panel is an overlay (no permanent cost), but the footer summary competes with the context meter on narrow widths; needs a fitFooter-style tier.
  • Nesting depth — teams are flat (lead + members); Task subagents can't recurse (child catalog excludes Task). A 2-level tree is sufficient today; don't build arbitrary depth.
  • Performance — MaxMembers = 32. Render from a per-member last-event projection (a snapshot), not by replaying buffered events each frame; cap the per-member focus buffer (reuse the maxToolResultLines idiom).
  • Child trace verbosity — showing every child tool.call risks re-bloating what Task exists to keep out of context; mitigate by keeping it opt-in (ctrl+t) + hard-capped + names/status only.
  • Event volume — subagent.tool per child call adds wire traffic; could gate behind a server flag and fall back to start/end-only.

5. Phased plan

  1. MVP — inline Task visibility. Forward subagent.start/tool/end from handleChildEvent; render the live sub-status line + terminal stat on the existing Task card. No new panel/key. Biggest win for the smallest change.
  2. Team consumption + footer summary. Wire client to the RunTeam stream, route by member, show the ⟳ team · k/N working footer segment.
  3. Agents panel (ctrl+a). The roster overlay, modelled on mcp.go, with per-agent state/activity/tokens/context meter.
  4. Member focus + task sub-view. enter to filter to one member; t for the shared task list. Add context_used/window to TeamMember if needed.

Key files

  • Inline card + events: internal/agent/subagent.go (handleChildEvent, drainChild), cmd/mecatui/ui/render.go (renderTool).
  • Panel reuse: cmd/mecatui/ui/mcp.go (overlay), cmd/mecatui/ui/footer.go (renderContextMeter/ctxLabel), cmd/mecatui/ui/keys.go.
  • Team data: internal/adapter/server/team.go (RunTeam/ListTeam), TeamEvent/TeamMember/TeamTask in contracts/proto/mecatl/v1/harness.proto.

Drafted from the mecatui UX gap analysis. The interface above is a proposal to spark implementation, not a final spec — the phased plan front-loads the highest-value, lowest-cost step (inline Task visibility).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    tuimecatui terminal UI (rendering, keybindings, footer, panes, scrollback)

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions