Watch Claude Code From Your Phone as a Conversation, Not an 80-Column Grid
Two of the most used coding agents shipped terminal-rendering work this week. Codex 0.156.0 added an optional fullscreen interface, six themes, and inline Mermaid diagrams and display equations. Claude Code 2.1.282, released today, added a setting that caps how wide the model's prose may run in a wide terminal. Both point the same way: an agent's output is prose, and a terminal is a grid. The gap is widest when you try to watch Claude Code from your phone. The shell.online app now has a renderer that reads the session as the conversation it is.
An agent session is a conversation drawn on an 80-column grid
The Codex changelog for 0.156.0 describes an optional fullscreen UI behind a /tui command, with transcript search and mouse selection, plus the ability to "view supported Mermaid diagrams and display equations directly in responses." The Claude Code changelog for 2.1.282 adds a maxProseWidth setting "that caps the width of Claude's prose in wide terminals while tables and code blocks keep the full width", next to fixes for garbled rows in the non-fullscreen renderer.
The agent writes headings, lists, fenced code, and paragraphs. The terminal turns them into rows of cells, and the agent's interface works hard to make those rows look like a document again. On a big monitor that mostly works. On a phone an 80-column grid is either unreadably small or wrapped into pieces that no longer line up. A terminal share does not change this by itself: the default xterm.js view shows the real screen byte for byte, which is right for anything interactive and poor for reading three paragraphs and a code block in a queue.
How to watch Claude Code from your phone as a conversation
The chat renderer lives in the shell.online app, the optional accounts layer, so the one-time setup is linking the machine. Everything else is the normal command.
curl -fsSL https://shell.online/install | sh
shell auth
shell --name "Refactor auth" claude
Linking publishes the share URL, the command name, the host name, and timing. It never publishes terminal contents or the browser password. On the phone, open app.shell.online, pick the session, and enter the eight-character password the CLI printed, or use the saved copy in your vault. In the terminal tab, change the Renderer selector from xterm.js to Chat. The choice is remembered per browser, and switching does not restart the process or touch the encryption.
What you get is a thread. Your prompts appear as sent messages. Claude Code's replies appear as received messages, with the Markdown it wrote rendered as headings, lists, inline code, quotes, links, and code blocks that carry their language. While the agent is working there is a Thinking line under the last message, driven by the spinner the program itself draws. Before this week a busy session, a finished session, and a broken session all looked identical on a phone: the thread simply stopped.
As the mobile guide says, closing the browser does not stop the host; the session ends when its process exits.
What the renderer reads, and what it declines to guess
The design is written up in the chat renderer README in the repository. It never parses the byte stream directly. It builds an xterm.js Terminal that is never opened, so it has no canvas and no DOM, and reads finished rows out of that emulator's buffer. Stripping escape codes and splitting on newlines fails within a minute of real use: a progress bar becomes two hundred lines, a wrapped line is cut in half, and the shell prompt becomes output. An emulator already solves all of that.
Two rules decide what is finished. The cursor row is never released, because whatever is on it is still being written. A wrapped line is released whole, because the emulator knows which rows continue the row above. Where a shell publishes OSC 133 command markers, the boundary between answers is exact.
Claude Code draws its own interface, so it needs an adapter on top, and that adapter is written against captured frames from the real program rather than an assumed shape. Three merges today tightened it. The title match now includes the rotating glyphs Claude Code puts in front of the window title once it has a task, so a session is still recognised after the header has scrolled away. And the repaint reader anchors on the tail of what it has already emitted rather than the head of the screen. In the replay test added with that change, three real exchanges went from 71 emitted utterances to 9.
The Markdown rendering is hand-written on purpose. The input is output from somebody else's machine, so every node is built with textContent and there is no path from the input to innerHTML. A link only becomes a link when its scheme is http or https. Raw HTML, images, reference links, and tables are left exactly as typed. The whole thing is in markdown.ts.
When a program takes the whole screen and no adapter can read it, such as vim, top, or a pager, the renderer prints one line instead of squeezing a grid into a bubble: "vim notes.md is running. Switch this session to the terminal renderer to see it."
Where the transcript lives after you reload the page
Reload the page and the renderer starts from whatever the relay replays, which is the current screen, not the conversation. The change that merged today keeps the conversation on the device that watched it, and the reasoning matters more than the mechanism.
The relay cannot read a session. The encryption page spells out what it does see: timing, addresses, encrypted sizes, labels, and lifecycle metadata. A server-side history would mean shipping the conversation somewhere it is not supposed to exist. The device that watched already has the plaintext on its screen, so it is the honest place to keep it.
The store is IndexedDB rather than localStorage. localStorage is synchronous, so every write would land on the thread that draws the conversation, and per MDN's storage quota documentation browsers allow about 5 MiB of local storage per origin. The cache keeps the most recent 400 messages and writes once a burst has settled, so an answer arriving a line at a time is one write rather than forty.
At rest, the cache is encrypted whenever the session is. The key is derived from the secret in the share link with HKDF-SHA256, per RFC 5869, under its own salt and info strings, so the cache key is never the session key. A cached conversation is therefore readable by exactly the people who could read the session it came from, and by nobody else with access to the disk. Reload with a different link and the cache cannot be decrypted, so it is treated as no history. A session started with --no-e2ee has nothing to derive from and is stored as plaintext, which is no worse than the session itself. The implementation is chat-history.ts.
A device that was not there says so. The first time a browser opens a session with an empty cache, the thread starts with "Nothing from this session is kept on this device. What follows starts here." That notice is not saved, because it is true of that moment and no moment after it. There is no syncing between devices: the relay broadcasts to every viewer, so each cache reflects every interaction that device was present for.
What access looks like once the session reads as chat
The URL and the password together are a bearer credential. Anyone holding both can read the session and, unless it was started with --read-only, type into it with the permissions of the process you wrapped. A conversation view does not narrow that in any way. It is the same session with a different renderer.
If you want a teammate to read the agent's reasoning from their phone without steering it, start the session read-only. A colleague who opens a session you handed over, through the organisation features covered in the earlier post on live terminal links, starts their own cache from that moment, and their thread will say so.
One note on versions. The CLI release is v0.23.1, published September 23, and none of the above needs a CLI update, because the chat renderer runs entirely in the browser. The adapter, Markdown, and history changes merged to main on September 24. The repository README is explicit that a merged change is not proof that every hosted component has been updated, so if the Renderer selector does not yet behave as described, the source linked above is the authoritative account of what shipped.
Read your agent, not its screen
Start Claude Code with one command, open the link on your phone, and switch the renderer to Chat. The process stays on your machine, the relay sees ciphertext, and the transcript stays on the device that watched it.
Try shell.online