Skip to content

Feature: watch the agent's shell commands run in a read-only terminal tab - #66

Merged
DevMando merged 2 commits into
mainfrom
feature/agent-output-terminal-tab
Sep 10, 2026
Merged

Feature: watch the agent's shell commands run in a read-only terminal tab#66
DevMando merged 2 commits into
mainfrom
feature/agent-output-terminal-tab

Conversation

@DevMando

@DevMando DevMando commented Sep 10, 2026

Copy link
Copy Markdown
Owner

Summary

The terminal panel gains a read-only tab per agent that shows the shell commands that agent runs, live, as they run. A ninety-second build is no longer ninety seconds of silence with a single truncated status line.

Why this matters

When an agent runs a build, a test suite, or a git command today, the user sees one status line, truncated to about eighty characters, until the command finishes. Then the finished output appears in the conversation. There is no way to watch a long-running command, no way to see how far a build got before it stalled, and no way to tell a slow command from a stuck one.

Everything needed to show that work already existed inside the command runner and was thrown away as it was produced. This puts it on screen.

What is new

  • A read-only "agent output" tab, one per agent. It shows the command and the folder it runs in, each line of output as it arrives with errors distinguished by color, and how the command ended — finished, finished with a failure, or killed for running too long. Each agent gets its own tab, since agents work in different folders and can run at the same time.
  • It records from launch, not from when you look. The terminal panel is only built the first time it is opened. Output is captured from the moment an agent starts working, so opening the panel after a build has already run still shows it. Retained history is bounded so a long session cannot grow without limit.
  • It shows more than the assistant is given. Output sent to the model is capped to protect its context window. This view keeps its own larger history, so the end of a long build is visible even when the model's copy was truncated.
  • It never interrupts you. A tab is created without switching to it, so an agent building in the background cannot pull the panel away while you are typing in a shell. The tab's title is accented when there is output you have not seen. Closing a tab discards that agent's recorded output rather than replaying it the next time a command runs.
  • The rail tells you it is there. The terminal button carries a dot when an agent has produced output you have not seen. Without it the feature would be undiscoverable — the tab lives inside a panel that is closed by default, so the only way to learn it exists would be to open the terminal for an unrelated reason and notice a tab you did not create. The dot is driven by command activity rather than by the panel, because the panel is not built until it is first opened, and the first command a user's agent runs is exactly the one that needs to advertise itself.
  • The dot pulses only while a command is actually running. Once it finishes, the dot goes still. Motion therefore means "work is happening now" and a still dot means "output is waiting" — two different facts that one badge would otherwise blur together. It is a slow pulse on purpose: a rapid blink reads as an error demanding action, and this is an invitation. Themes that switch motion off get the still dot in both states, and the animation is stopped rather than merely hidden when idle so nothing animates off-screen.
  • Following the dot lands you on the output. Opening the panel with output waiting brings that agent's tab to the front. Previously the panel always opened its starter shell first and attached agent tabs afterward, so following the cue dropped you on an empty prompt with the tab you wanted hidden behind it. Opening the panel with nothing waiting still gives you a shell as before.

What deliberately did not change

The agent still runs commands the way it always has, through captured pipes rather than a real terminal. That is what keeps normal output and error output separate, keeps the text free of terminal control codes, and leaves a genuine exit code to report.

Running the agent through a real terminal to make it visible was considered and rejected: it would have degraded the output the model reads, replaced a reliable exit code with prompt-parsing guesswork, broken the existing timeout and kill behavior, and put the user and the agent in a race to type into the same session. This change adds a second reader of information that already existed, so it costs the agent's reliability nothing.

The direct consequence, worth stating plainly: the tab is read-only and cannot be typed into. A command that stops to ask a question still cannot be answered, and still times out as it does today. Making that answerable is a separate piece of work with a very different risk profile.

Scope and risk

Medium, and concentrated in the terminal panel rather than in the agent.

  • The terminal panel now hosts tabs with no shell behind them. Its tab model previously assumed every tab owns a shell process. Input handling, resizing, closing, and shutdown are all now null-safe for a tab that has none. A reviewer should confirm ordinary shell tabs are unaffected: opening several, switching, resizing the panel, closing them, and closing the window.
  • Output arrives on background threads. Each line is handed to the UI thread before touching the terminal. The buffer is held under a lock, so a dropped hand-off costs a live update, never recorded history.
  • Command output is stripped of control characters before display. A tool that emits terminal codes even when not attached to a terminal could otherwise clear or scroll the log being read. Tabs are kept; anything that could move the cursor is neutralized. This is display hygiene, not a security boundary — the same text already reaches the model unchanged.

Verification

Desktop builds clean (0 warnings, 0 errors) and the full suite passes — 345 of 345, including 15 new tests covering how a command's lifecycle is rendered, that errors and successes are distinguishable, that output cannot drive the display, that history is bounded and keeps the newest output, that trimming never leaves a broken color code behind, and that the running state driving the pulse survives overlapping commands and cannot be wedged on by an unbalanced call.

An earlier revision of this branch was exercised by hand and confirmed working: live output arriving in its own tab, errors and exit codes rendering distinctly, the idle-timeout kill path, per-agent tabs, read-only input, closing a tab starting clean, and output recorded before the panel was first opened. The rail badge and landing-tab behavior described above were added after that pass and need their own.

Not covered, and needing a manual pass: the panel and the rail are XAML plus a WebView and are not reachable from tests. A reviewer should confirm the badge appears when an agent runs a command with the terminal closed; that it pulses while the command runs and goes still when it ends; that following it opens the panel directly on the agent's tab and clears the dot; that opening the panel with no dot still lands on a shell; and that the badge stays lit if the panel is opened onto a shell tab, since having the panel open is not the same as having read the output.

Dependency

Merge DevMando/MandoCode#95 first — this depends on the command output sink added there. The submodule pin in this branch points at that PR's branch commit so the build works now; once #95 merges, the pin needs re-pointing at the resulting commit on the engine's main before this is merged.

The agent's commands were invisible while they ran: a long build was a
single truncated status line for its whole duration, then a wall of finished
output.

Give each agent a read-only tab in the terminal panel, fed by the engine's
new command output sink. Deliberately NOT a shared pseudo-terminal: the
agent keeps running commands through captured pipes, which is what keeps
stdout and stderr separate, keeps the output free of escape sequences, and
leaves a real exit code to report. Routing the agent through a PTY to make
it visible would trade all three away for cursor control the model has no
use for, plus a second writer racing the user for one stdin. This is a
fan-out of lines that already existed, so it costs the agent nothing.

AgentCommandLog buffers per agent from launch, because the terminal panel is
built lazily — without it, an agent that built before the panel was ever
opened would show an empty tab, which is the case the feature exists for. It
holds more than the model's 5000-character copy, so the tail of a long build
stays visible. Trimming lands on a line boundary: cutting mid-escape would
leave half an SGR code to colour everything after it.

AgentCommandFormat renders the lifecycle and strips control characters from
command output, so a tool that emits VT when it isn't talking to a terminal
can't clear the log someone is reading.

Tabs never steal focus — an agent building while you type in a shell must
not yank the panel away — so the title accents instead. Closing a tab clears
that agent's buffer, so it isn't replayed on the next command.
The output tab was undiscoverable. It lives inside a panel that is closed by
default, so the only way to learn it exists was to open the terminal for an
unrelated reason and notice a tab you didn't create.

Badge the rail's terminal button when an agent has produced output the user
hasn't seen. The rail is always visible, so the cue arrives where they are
already looking, at the moment it becomes true. It is driven by command
activity rather than by panel state on purpose: the panel isn't built until
first opened, and the first command a user's agent runs is precisely the one
that has to advertise a tab they don't know about.

The dot pulses only while a command is in flight and goes still once it
finishes, so motion means 'work is happening now' and a still dot means
'output is waiting' — two facts one badge was otherwise conflating. Unhurried
on purpose: a fast blink reads as an alarm, and this is an invitation. The
storyboard is stopped rather than hidden when idle, and FlatMotion themes get
the still dot in both states rather than a pulse they've opted out of.

Track in-flight commands with a counter, not a flag: a plan step can have one
command running while another closes out, and a flag would stop the pulse
with work still going. Clamped at zero so an unbalanced finish can't wedge
the rail pulsing forever.

Also fixes the landing tab. The panel creates a starter shell when it opens
and agent tabs are attached after that, so following the badge dropped the
user on an empty prompt with the tab they came for hidden behind it. Opening
with unread output now brings that tab to the front. Gated on unread rather
than on a tab existing, so reaching for Ctrl+` to get a shell still gets one.
Wired into both the first open and every reopen, since Ready fires once.
@DevMando
DevMando merged commit 99bb196 into main Sep 10, 2026
1 check passed
@DevMando
DevMando deleted the feature/agent-output-terminal-tab branch September 11, 2026 03:46
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.

1 participant