Lesson

Fleet Dashboard

A TUI widget that shows all active agents — status, elapsed time, last activity — without switching workspaces.

You've got agents running across multiple workspaces. Each one updates its sidebar status, broadcasts completion, and responds to orchestration commands. But there's a gap: to see the full fleet, you have to mentally aggregate sidebar signals or ask the LLM to run list_agents. What you need is a single view that shows everything — a dashboard that auto-updates without requiring explicit queries.

This lesson covers the design for that dashboard. It's partly forward-looking — the widget isn't built yet — but you'll understand exactly what needs to exist and why the current tools are actually the MVP version.

Two rendering surfaces

A key architectural discovery emerged during development: pi-cmux and the gremlin extension use completely different rendering layers that coexist in the same Pi process. Understanding both is essential for fleet visibility.

The cmux sidebar — cross-workspace broadcast

Every workspace has a sidebar. You set status with cmux set-status:

cmux set-status "state" "Running" --icon "bolt.fill" --color "#4CAF50"

The sidebar is cross-workspace visible. Switch from "orchestrator" to "worker-1" and you still see the status that "worker-2" set. This makes it the quiet ambient signal — the heartbeat that tells you something is happening without pulling your attention.

For agents, each worker broadcasts its own status:

  • Running with bolt.fill icon when executing tools
  • Idle with pause.circle when waiting for the orchestrator
  • Needs input with exclamationmark.triangle when blocked

The orchestrator's sidebar shows the fleet count: "2 agents" with person.3.fill icon.

The Pi TUI widget — in-session rich view

Pi extensions can render widgets in their own session using ctx.ui.setWidget():

ctx.ui.setWidget({
  title: "Build Phase",
  content: "● Research  ◉ Design  ● Build  ◯ Review",
  height: 3
});

The widget appears above the editor, but only in the session that registered it. Switch to a different workspace and the widget disappears — it's session-local, not cross-workspace like the sidebar.

The gremlin extension demonstrates this with phase indicators and agent team dots. The widget shows rich, updating state that would be too noisy for the sidebar.

What exists today

The current toolset already provides fleet awareness, just through different interfaces:

LLM-driven inspection

The list_agents tool returns structured fleet state:

// What the LLM sees when it calls list_agents
[
  {
    "agent_id": "1a0g6g1w",
    "workspace": "worker-auth",
    "model": "claude-3-5-sonnet-20241022", 
    "status": "running",
    "created": "2024-03-19T15:32:10Z",
    "last_activity": "2024-03-19T15:33:45Z"
  }
]

The orchestrator can call list_agents followed by read_agent to inspect any worker's current state. This is the dashboard — it's just LLM-driven instead of auto-updating.

Completion detection

When agents finish tasks, they trigger orchestrator turns:

pi.sendMessage({
  triggerTurn: true,
  content: `Agent ${workerId} completed task: ${summary}`
});

This creates notifications in the orchestrator session. State changes are actively pushed, not just visible through polling.

Sidebar fleet count

The orchestrator's sidebar shows the fleet summary: "2 agents" with an icon. It's the ambient signal that agents exist, even when you're not looking at the dashboard.

The gap that creates Phase 2

What's missing is automatic visual aggregation. The current tools require:

  • The LLM to actively call list_agents to check status
  • The human to mentally aggregate sidebar signals across workspaces
  • The orchestrator to ask questions like "what agents are running?"

There's no single auto-updating view that shows the full fleet at a glance.

The widget design

Phase 2 fills this gap with a TUI widget in the orchestrator session:

┌─ Fleet ─────────────────────────────────┐
│ ● 1a0g6g1w  opus    2m 14s  Editing…    │
│ ◉ 9b2c3d4e  sonnet  45s     Idle        │
│ ⠋ f3g4h5i6  haiku   12s     Starting    │
└─────────────────────────────────────────┘

Each row shows:

  • Status icon — ● running, ◉ idle, ✔ completed, ✗ failed, ⠋ starting
  • Agent ID — the short identifier from agent spawning
  • Model — opus/sonnet/haiku for quick capability assessment
  • Elapsed time — how long since this agent started
  • Current activity — last tool call or "Idle" state

Adaptive polling

The widget doesn't poll at a constant rate:

  • 3 seconds when any agent is running (responsive during active work)
  • 15 seconds when all agents are idle (reduce noise)
  • Off entirely when fleet is empty (no wasted cycles)

Completion detection already handles the start/stop pattern — when the first agent spawns, polling begins. When the last agent completes, polling stops.

Implementation sketch

The widget would use the same data source as list_agents:

async function updateFleetWidget() {
  const agents = await listAgents();
  const rows = agents.map(agent => 
    formatAgentRow(agent.status, agent.agent_id, agent.model, agent.elapsed, agent.activity)
  );
  
  ctx.ui.setWidget({
    title: "Fleet",
    content: formatTable(rows),
    height: Math.min(agents.length + 2, 8)  // Cap at 8 lines
  });
}

The polling loop would integrate with the existing completion detection system — same triggers, same lifecycle, just an additional subscriber to fleet state changes.

Two surfaces, one system

The fleet dashboard demonstrates why both rendering layers exist:

  • Sidebar status — Per-agent state visible cross-workspace. Quiet, persistent, always available. Perfect for "is anything running?" awareness.

  • TUI widget — Rich aggregate view in the orchestrator session. Detailed, updateable, contextual. Perfect for "what exactly is each agent doing?" monitoring.

Neither replaces the other. The sidebar provides the ambient signal; the widget provides the detailed view. Together they create comprehensive fleet visibility.

The progression so far

Look at what each module has built:

  • Module 0 (Terminal Fabric) — Made individual agents visible through cmux surfaces and sidebar status
  • Module 1 (Spawning Workers) — Made agents spawnable and controllable through tools and lifecycle management
  • Module 2 (Eyes on the Fleet) — Made the fleet observable through completion detection, attention models, and dashboard design

Each module solves a visibility gap the previous one created. You can spawn agents, but can you see them? You can see them individually, but can you see the fleet? Each layer of the system builds on the foundation below.

What comes next

After fleet visibility comes fleet coordination — the patterns for decomposing work, routing tasks, and managing dependencies between agents. The dashboard becomes the control surface where you see not just what agents are doing, but how their work fits together.

The widget design in this lesson isn't implemented yet, but the data layer already exists. Every piece you'd need — agent enumeration, state polling, completion detection, sidebar synchronization — is already working. Phase 2 is a rendering problem, not an architecture problem.

That's the power of building in layers. By the time you need the dashboard, all the hard parts are already solved.