Marspot
Written in Rust for macOS. Each pane is its own process, nothing animates when nothing changes, and the thing running in a pane is something the terminal knows about rather than text scrolling past.
Each pane owns its own PTY in its own process. The window process draws and nothing redraws unless something changed. A pane that crashes does not take the window with it, and idle panes cost close to nothing.
Marspot knows which agent is running where, what it is doing right now, and what it is costing. It can act on that: move a session off an account that has run out of quota and pick the conversation back up, hand work from one agent to another, park a pane that has gone quiet and bring it back on a keystroke.
| stream | Marspot | Ghostty 1.3.1 | iTerm2 3.7.3 | Terminal 2.15 |
|---|---|---|---|---|
| ASCII | 128.0 | 97.0 | 84.2 | 43.2 |
| mixed | 128.0 | 88.9 | 24.1 | 36.0 |
| CJK | 133.3 | 118.5 | 10.0 | 35.6 |
| emoji | 128.0 | 110.3 | 0.5 | 38.6 |
What this measures, and what it does not.
It measures how fast a terminal reads and parses a pseudo-terminal. It says nothing about frame rate or about the delay between a keystroke and a character appearing. A terminal that combines several updates into one frame does less drawing work here, and that is a reasonable design choice rather than cheating.
One run, one machine, one day. Repeatability has not been established, and three of the four numbers landing on exactly 128.0 says the timer's resolution was the limiting factor at these speeds — the harness read a clock with two decimal places, and a tenth of that is several MB/s here. It reads a microsecond clock now, and this table predates the fix: treat these numbers as a floor with an unknown error bar until they are measured again.
The harness is in the repository. A benchmark you cannot re-run is a marketing claim, so the intention is that you re-run it.
Throughput is the number every terminal benchmark reports, and it is not the one that decides whether a machine with twenty sessions open is pleasant. That number is what the thing costs while you are not typing, and it is the reason this terminal exists.
Nothing here animates. There is no cursor-blink timer and no frame loop: a pane redraws when its contents changed and not otherwise, so a window full of idle panes draws nothing. One timer does remain — the process that owns the window wakes once a second to refresh what it shows about the panes — and saying there are none would be a tidier sentence than a true one. Each pane's parser, grid and scrollback live in their own process, which is what makes the cost per pane flat rather than compounding, and what keeps a crash inside one pane.
What is measured, and what is not yet.
The repository gates per-pane resident memory: a cap, and a linearity bound that fails if the cost per pane grows with the pane count. It is checked at one, four, nine and sixteen panes — the counts a 2×2, 3×3 and 4×4 grid actually produce. Bounded disk and bounded scrollback are gated the same way, because a terminal that gets heavier the longer it runs has only moved the problem.
What is not here is a table. Comparing idle cost across terminals needs a machine doing nothing else — otherwise the scheduler charges one terminal for another's work — and every terminal measured the same way. The harness exists; the quiet machine is the part still owed. Until then this section has no numbers in it, which is the honest state rather than a modest one.
Everything else goes to the program in the pane, unchanged.
| ⌘N | New window |
| ⌘C | Copy the selection |
| ⌘V | Paste |
| ⌘F | Search this pane's scrollback |
| ⌘B | Show or hide the sidebar |
| ⇧⌘C | Agent usage for this account |
| ⌘W | Close the panel that is open |
| Esc | Close the panel that is open; three times in five seconds ends a stuck agent session |
Pre-1.0, and used daily by the people writing it. These are the things you would hit in the first hour, listed because finding them yourself is worse.