Skip to content

Add Agent Fleet: a live grid of every agent pane - #382

Open
0x92 wants to merge 1 commit into
dcouple:mainfrom
0x92:feature/agent-fleet
Open

Add Agent Fleet: a live grid of every agent pane#382
0x92 wants to merge 1 commit into
dcouple:mainfrom
0x92:feature/agent-fleet

Conversation

@0x92

@0x92 0x92 commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Description

Running several agents at once means switching sessions to find out which one is
waiting for an answer. This adds a top-level view that shows them all at once,
grouped by project, status or agent.

  • Grid of tiles, one per agent pane, with status accent, branch and age
  • Cheap ANSI-stripped snapshots by default; a tile becomes a real xterm on hover,
    or all of them with "All live"
  • Type into a focused tile without leaving the grid, optionally expanding it to
    the agent's full screen
  • Fold projects away, jump to the next blocked agent (N), move with the arrow
    keys, close a pane
  • Extracts the screen-text selection shared with the RunPane CLI into
    services/panels/terminalScreenText.ts

Two constraints are load-bearing and documented where they are enforced: a tile
renders at exactly the PTY's dimensions (agent TUIs use absolute cursor
positioning, so a mismatched width wraps every line and scrolls forever), and a
tile never answers the agent's terminal queries — the real panel is the single
voice on that PTY.

Type of Change

  • New feature (non-breaking change which adds functionality)

Checklist

  • I have read the CONTRIBUTING.md guidelines
  • My code follows the code style of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • My changes generate no new warnings
  • New and existing unit tests pass locally with my changes
  • I have run pnpm typecheck and pnpm lint locally
  • I have tested the Electron app locally with pnpm electron-dev
  • I have added tests that prove my fix is effective or that my feature works
  • I have made corresponding changes to the documentation (AGENTS.md)

Critical Areas Modified

  • State management/IPC events (two new fleet: channels, registered through
    the shared command registry and covered by daemonRegistryBindings.test.ts)

Screenshots (if applicable)

image

Additional Notes

Tests: fleetGrouping (14) covers grouping, ordering and pane labelling.

These four feature PRs are independent but all add an entry to the same
navigation plumbing (navigationStore's ActiveView, the two sidebar
components, preload.ts, api.ts, electron.d.ts). Whichever lands first,
the others need a small rebase there — no logic overlaps.

Not done: the packaged-build check from CONTRIBUTING. I develop on Windows,
so pnpm build:mac was not run.

Running several agents at once means switching sessions to find out which
one is waiting for an answer. This adds a top-level view that shows them
all at once, grouped by project, status or agent.

- Grid of tiles, one per agent pane, with status accent, branch and age
- Cheap ANSI-stripped snapshots by default; a tile becomes a real xterm on
  hover, or all of them with "All live"
- Type into a focused tile without leaving the grid, optionally expanding
  it to the agent's full screen
- Fold projects away, jump to the next blocked agent (N), move with the
  arrow keys, close a pane
- Extracts the screen-text selection shared with the RunPane CLI into
  services/panels/terminalScreenText.ts

The viewer renders at exactly the PTY's dimensions and never answers the
agent's terminal queries — both are load-bearing, and both are documented
where they are enforced.

Tests: fleetGrouping (14) covers grouping, ordering and pane labelling.
@parsakhaz

Copy link
Copy Markdown
Member

Thank you for this, @0x92. Running four agents and having to visit each one to
find out which is waiting is exactly the problem worth solving, and three people
have now asked for it. The two constraints you found and documented are the
reason the thing actually works: a tile rendered at the PTY's exact dimensions,
and a tile that stays silent on the agent's terminal queries so the real panel
remains the single voice on that PTY. Both are now written down in AGENTS.md
where the next person will find them.

I have opened #484 with your commit cherry-picked
as the base, so the authorship and the history stay yours. It carries three changes on top:

  • Conflicts resolved against current main, which moved under the branch
    (title-bar context, Window Controls Overlay, and the animation work all
    touched the same shell chrome).
  • The feature ships as Mission Control, which is the name we are using for
    this surface across the product. The rename covers the strings, the files, and
    the identifiers, in its own commit so your work stays readable underneath it.
  • A cleanup pass for the gates main added after you opened this: the anti-slop
    and Knip rules, boundary-decoded IPC payloads, and guarded persisted view
    options. It also fixes one real defect: the stale-viewer sweep compared a
    prefix that could never match a minted viewer id, so the prune timer was inert
    and a crashed renderer left hovered panels pinned visible. Both sides now read
    one shared constant, and a test holds it there.

A couple of things I would rather hear your view on than decide for you: whether
a stopped agent's tile should show its persisted transcript at all, since without
a live emulator the ANSI strip runs words together; and whether a tile should
keep acking terminal output when the same panel is already open in a real
terminal panel, which currently credits flow control twice.

Thanks again for the contribution, and for the care in the commit message.

@0x92

0x92 commented Aug 21, 2026

Copy link
Copy Markdown
Contributor Author

@parsakhaz Thanks and no objection to Mission Control or the rebase. #484 reads better than
what I opened, and keeping the rename in its own commit was a kindness.

Stopped tiles:
Yes, show it, your screenText fix mostly answers this, since the
mangled text was the only reason I hesitated. What's left is the fallback:
screenText exists only if an emulator ever ran for that panel, so one restored from
before this lands, or an agent that exited while Pane was closed, still drops to
alternateScreenBuffer and the run-together text. selectPanelScreenText returns
both as persistedOutput, so the tile can't tell them apart. If that source split
into "laid out by an emulator" and "reconstructed from a byte log", the tile could
show a plain "stopped no screen captured" instead. Garbage on a tile costs more
trust than an empty one.

Acking: agreed. One designated consumer, preview last, is the right shape.

One thing I'd check. Nothing seems to credit the bytes a handover strands.
pendingBytes is debited once per chunk and only comes down through onAck. When
designation moves, a sole-viewer tile losing the panel to a session the user just
switched into, or the Remote PWA arriving, the old consumer's in-flight acks hit
if (designated && …) return and are dropped.

That's a few KB per flip, and disposeFlowControlRecord is the only thing that ever
zeroes the counter. Past LOW_WATERMARK it costs a 5-second stall on every burst that
trips the pause. Past HIGH the stall stops going away.

Zeroing pendingBytes on a designation change should cover it, the incoming consumer
hydrates from a replay anyway, so it isn't owed credit for anything before it arrived.
I haven't reproduced this, so treat it as a reading of the code rather than a report.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants