l0ng-ai 07e3b26434 feat(agent): outline a coding agent's conversation, and jump back to a turn (#703)
* feat(agent): outline a coding agent's conversation, and jump back to a turn

The hooks tty7 installs into Claude Code already announce every turn over the
pty as an OSC 777, and the daemon reads those for the pane's status dot. The
same bytes reach the client, where they are worth something else: the byte
offset a `prompt-submit` lands on is a *position in the stream*, so advancing
the emulator to exactly there and reading the cursor gives the scrollback row
that turn began on. That is an outline of the conversation, and a way back into
it — which is the one thing a long agent session in a terminal has never had.

The Info panel grows a CONVERSATION section: one row per turn, the prompt's
first line as its label, a dot that says whether the turn is still running.
Clicking a row scrolls the pane so that turn's prompt is the top line.

Not a fourth right-panel tab. `RightPanelTab` says out loud why there is no
room for one at 260px, and a fourth variant would drop anyone who rolled back
to an older build onto Info. This is a fact about the pane, like its shell and
its cwd, so it sits with them.

The hook is a subprocess writing to the controlling tty while the agent's own
renderer writes to it too. Claude Code repaints in place with ink, so the cursor
when the hook's bytes land is wherever the last repaint left it — inside the
live region, a few rows from where the prompt's echo comes to rest. And once
the scrollback limit starts discarding lines, every anchor slides by the discard
count at once.

So the anchor is a hint, and the prompt's own text is the correction: at click
time (by which point it has long been drawn) the row is looked for around the
anchor, exact match first — the row that *is* `> hi`, marker stripped — and only
then by containment, which keeps its length floor because `hi` appears inside
half the rows of any answer. The row that is found is written back, so a second
click does not search again and cannot land somewhere else.

Claude Code keeps a JSONL transcript, and reading it would give the assistant's
side too. It would also only work for Claude, only when the agent runs on this
machine, and only for a path this process may read. An OSC comes back through
the pty from wherever the agent actually runs — over ssh, in a container, in a
remote workspace — with no file access and no per-agent format. What is lost is
the assistant's text; what is kept is every host tty7 supports.

- `OscTokenizer::feed_at` reports each payload's end offset. The client already
  tokenized OSC 777 on every batch to keep agent events out of desktop
  notifications, so the scan is free; only a real event now costs a cut, which
  is what #404 was right to object to about the old per-command mark scanner.
- `Cut` is a two-variant enum again (cursor repair, agent turn). Two ascending
  runs concatenated are not one, so a batch carrying both kinds is sorted —
  and only such a batch pays for it.
- A replayed ring is cut the same way, so reattaching to a pane rebuilds the
  outline from its own history rather than losing it with the old client.
- Turn anchors are dropped where image placements are: `clear_scrollback`, and
  the grid reset in `adopt_relink`.
- A turn that began on the alt screen is listed but not clickable — there is no
  scrollback behind it to return to.
- A turn announced twice is one turn. Hooks are not guaranteed to fire once,
  and what makes it the same turn is that the one before it never ended: a real
  repeat can only come after an answer, and an answer brings a `stop`.
- The hook forwards the prompt's first line, clamped to 200 characters. The
  tokenizer *abandons* a payload past 8 KiB rather than truncating it, so a
  pasted file would otherwise cost the whole event; and a needle spanning a line
  break matches no single row.

No protocol change: the prompt rides in the OSC the hook already sent, and an
older client ignores the field.

* refactor(panel): drop the Info panel's agent row

It said `Claude Code · working` behind a status dot — the same name and the
same dot the tab chip and its sidebar row were already wearing, restated two
panels away from either of them. The CONVERSATION section that now sits under
it says what the agent is doing in a form the row never could: which turns
there were, which one is still running, and a way back to each.

`InfoValue::Agent` and `status_pip` went with it — the dot was the row's only
caller — and `PanelAgent` / `PanelAgentIdle` with those. The remaining three
status labels stay: the tray menu still names them.

`Tab::agent_row` stays too. `agent_status` is that pair's status and the tab
strip's badge reads it, which is the one-leaf rule #543 put there.
2026-08-20 21:56:34 +08:00

tty7

tty7

A terminal workbench: persistent sessions, remote work, agents.

Pure Rust · GPU rendering on Zed's gpui · VT core from Alacritty


CI Version Platforms License Discord

English · 简体中文


tty7 with a tab sidebar of agent sessions across several repos, running Claude Code

Why

A background server owns your shells and panes — not the window. Everything below follows from that.

  • Performance — ~2× the throughput of Alacritty, Ghostty, or Kitty (benchmarks)
  • Persistent sessions — quit or reboot; your shells and supported agent sessions keep running, no tmux
  • Agent-aware — Claude Code, Codex & co.: status, notifications, and git context for every repo at once
  • Scriptable by agents — one agent opens a pane for another, hands off a task, waits, and reads the result, with or without the GUI running
  • Editor-grade input — suggestions, completion, highlighting, history search, with no plugin to install
  • Remote development — files, repos, panes, and git data stay on the remote machine, over a native SSH stack
  • Git beside the terminal — source control, diffs, and worktrees without leaving the window

Install

Native builds for macOS, Windows, and Linux on Releases:

macOS …-macos-arm64.dmg · …-x86_64.dmg drag into Applications
Windows …-setup.exe · portable ….zip
Linux …-x86_64.AppImage chmod +x and run — X11/Wayland libraries bundled

What's inside

Agent-aware per-pane detection (19 CLIs) · status dot · notifications · branch + diff · tray icon when input is needed · resume after reboot · tab sidebar grouped by repository
CLI + Skills bundled tty7 CLI · agent skill · run streams a command and exits with its code · split · send · wait --until free · capture
Editor-grade input ghost suggestions from history · explained tab completion · syntax highlighting · multi-line editing · click places the caret · ⌃ R fuzzy history
Window tabs & splits · ⌘ P palette · ⌘ F scrollback search · ⌘ J panel with process tree and listening ports · 13 themes, your own YAML, iTerm2 import · IME
Shell integration injected when a pane starts, nothing to install · prompt marks · working directory · exit codes · command-finished notifications · zsh, bash, fish, PowerShell, WSL, remote panes
Remote workspaces remote files, repos, changes, diffs, worktrees, tabs, and panes · reconnect from any client and continue where you left off
SSH native russh stack: profiles with keychain secrets · SFTP panel · port forwarding · jump hosts · one-time, unprivileged tty7-server install
Git panel follows the focused pane · stage, commit, amend, branch, push, stash · side-by-side or unified diffs · commit graph with cherry-pick, revert, and reset · a new worktree opens its own tab

Supported agents

Detection is free: brand avatar, branch + diff, tab title. Status takes one click under Settings → Agents to install that agent's hook, and brings the status dot, notifications, the tray icon, tty7 wait, and resume after a reboot. Fork needs both — the agent's own fork command, and the hook that tells tty7 which session to fork.

The full support matrix, all nineteen
Agent Detected Status · resume Fork
Claude Code
Codex
Grok
OpenCode
Oh My Pi
Droid
Qwen Code
Goose
Gemini
Copilot
Kimi Code
Pi
Aider
Amp
Cursor
Auggie
Hermes
Vibe
Antigravity

None of them are wrapped or proxied — the agent you start is the agent you get, in a normal PTY, with its own interface. An agent launched through a wrapper script can be mapped to one by name with agent_commands in config.json.

Documentation

Full documentation lives in docs/keyboard shortcuts · config.json · CLI reference. The agent-facing CLI interface is also documented in skills/tty7/SKILL.md.

Install the skill with:

npx skills add l0ng-ai/tty7    # install
npx skills update tty7         # update later

Benchmarks

Same machine, same day, same 155×40 grid — Apple M1 Pro, macOS 26.3.1, five-run averages (2026-07-04):

tty7 Alacritty Ghostty Kitty
Plaintext I/O — 11 MB cat (lower = better) 95 ms 239 ms 179 ms 185 ms
DOOM-fire frame rate (higher = better) 888 fps 485 fps 552 fps 617 fps
Cold-launch memory 116 MB¹ 105 MB 128 MB 130 MB

¹ GUI 105 MB + the persistent server 11 MB.

Methodology and one-command reproduction: scripts/bench/.


S
Description
A terminal workbench in pure Rust: shells, persistent sessions, SSH, coding agents. GPU-rendered on Zed's gpui, VT core from Alacritty.
Readme Apache-2.0
34 MiB
Languages
Rust 99.1%
Shell 0.5%
PowerShell 0.2%
Inno Setup 0.1%