Keep a Terminal Session Alive After a Disconnect: What a Long Agent Run on shell.online Survives

Keep a Terminal Session Alive After a Disconnect: What a Long Agent Run on shell.online Survives

On October 8, at its Gemini at Work event, Google Cloud announced a Gemini agent built to handle tasks "lasting hours or days", according to VentureBeat's report on the persistent Gemini agents. It runs in the cloud, so you can start an assignment on one device and resume it on another. CBS News quoted Google Cloud CEO Thomas Kurian: "You never have to re-brief it or wonder which machine you told it to do something on." That is one answer to long-running agent work: put the process somewhere that never sleeps. shell.online gives a different answer. The process stays on your own computer and only the view moves. So if you want to keep a terminal session alive after a disconnect, you first need to know which disconnect you are dealing with. Below is each case, with the numbers taken from the source.

Keep a terminal session alive after a disconnect: first, which connection dropped

Say you start an agent with a browser link to a terminal process:

shell --name "migration" claude

The CLI starts Claude Code under a PTY on your machine, opens a WebSocket to the relay, and prints a link, a ten-character password and a QR code that holds both. The session runs in the background, so your terminal comes back immediately. Your phone then opens its own WebSocket to the same relay. Frames are end-to-end encrypted by default: the CLI seals them before they leave the machine and the browser opens them, so the relay forwards bytes it cannot read. The encryption details explain how the key comes from the password and the salt in the URL fragment. New to the tool? Our introduction to shell.online covers the basics.

Two sockets means two different things can fail: the viewer's connection or the host's. The relay includes the host's state in every status message it sends, and the browser uses it, so a viewer can tell which of the two it is looking at.

The phone locks or switches networks

This is the most common case and the least serious. The browser's socket dies. The process on your computer never notices, and the agent carries on. The connection troubleshooting guide says the CLI and the browser both retry temporary failures automatically, and that current hosts keep bounded screen state and restore the screen when viewers come back. Reopen the tab and the same link and password still work, and you get a fresh snapshot of the current screen.

What you do not get is every line that scrolled past while you were gone. Display queues are bounded. When output outruns the connection, the guide says, stale display work may be replaced by a fresh screen snapshot "rather than an unlimited transcript." If you need a build log in full, also write it to a file and read that:

shell -- sh -c 'make 2>&1 | tee build.log'

The host loses its network

Now the process is still running, but the relay cannot reach it. The CLI's reconnect loop is short enough to read in one sitting in internal/relay/relay.go in the shell.online source. When a connection drops, it waits 500 milliseconds, doubles the wait after each failed attempt, and caps the wait at 10 seconds. A successful connection resets it to 500 ms. Once the machine's network is back, the next attempt is never more than ten seconds away, and you do nothing.

There is one exception: the first connection is not retried. If the relay is unreachable when you run the command, it fails with the dial error. You do not get a link that could never work.

The viewer sees something explicit in the meantime. Pull request 168, merged on September 15, fixed a bug where a viewer with a perfectly good connection to a rebooted machine got only a blank white rectangle. The relay now keeps the last full screen the host sent and replays it to anyone who opens the link while the machine is away. That kept screen is dimmed so nobody mistakes it for a live one. Above it is a "Temporarily offline" notice saying how long ago the machine was last seen. A host only sends a screen when asked, so while it is connected the relay requests a fresh one every five minutes, which caps how stale the kept copy can get.

For an encrypted session the kept screen is ciphertext. The opcode is authenticated as associated data, so the relay stores the frame and replays it untouched. With --no-e2ee the kept screen is plaintext, held on the relay for as long as the session exists. That is the same trade the flag already makes for data in flight, and the CLI prints a line warning that the relay can read terminal plaintext whenever you use it.

The laptop goes to sleep

Nothing above helps with a sleeping host. Sleep suspends the process along with everything else, so the agent stops working. That is why the CLI prints "Keep this computer awake and online. Closing the browser does not stop it." every time it starts a session. From the phone, a sleeping laptop looks the same as the network case: a dimmed screen and the offline notice.

On macOS, caffeinate holds a power assertion for as long as a utility runs. The catch is that shell starts the session in the background and returns, so caffeinate -i shell claude would drop its assertion almost at once. Keep the CLI in the foreground instead:

caffeinate -i shell --foreground claude

Per the man page, -i prevents idle sleep and -s prevents system sleep, but only on AC power. On Linux with systemd, systemd-inhibit does the same job. It takes an inhibitor lock before the command starts and releases it when the command exits:

systemd-inhibit --what=idle:sleep --why="agent run" shell --foreground python train.py

--foreground returns the wrapped process's exit status, so either line also works inside a script.

The link and password together are a bearer credential, and the QR code carries both. Anyone who has them can watch and, unless the session was started with --read-only, type with the permissions of the wrapped process. A run that lasts a day is exposed for a day. If you only need to check on it, start it with shell --read-only, and run shell password rotate <ID> to disconnect old viewers without stopping the process.

The twelve-hour clock, and what --persistent changes

The relay does not keep a session open forever for a host that never comes back. In shared/session-lifetime.ts, an ordinary session gets 12 hours and a persistent one gets 30 days. While the host is connected, that deadline keeps moving forward. Once the relay notices the host is gone, it sets the session to expire 12 hours later. If the machine comes back inside that window, the CLI reconnects to the same session and the same link works. After the window closes, the share is gone even if the process is still running locally. Separately, an ordinary share closes when its wrapped process exits.

If you can still get to the machine, you do not need the relay to check on the session at all:

shell list
shell attach <ID>

attach joins from a local terminal, and Ctrl-X then D detaches without stopping anything.

For work that has to survive a restart of the host itself, --persistent keeps one URL and password across restarts:

shell --persistent ./train.shell-state python train.py

After a clean restart, run the same command with the same file and the old link comes back. The command reference is clear about what this does not do. The owner-only state file holds host credentials, the browser password and decryption material, so treat it like a key. It restores the link, not the process: a training job restarts from whatever checkpoint your own code saved. If a crash leaves a local record or control socket behind, the resume is blocked until you review it. The CLI never silently reuses one.

What this setup does not do

  • It does not move the process. Google's agent runs in the cloud. A shell.online session runs only on the machine where you started it, and if that machine is off, so is the agent.
  • It does not replay missed output. Reconnecting shows you the current screen, not a transcript.
  • It does not checkpoint. --persistent restores a link and nothing in memory.
  • It does not keep a dead share alive. After 12 hours away (30 days with --persistent), the link expires.
  • It does not wake the machine. The README says it plainly: a sleeping or offline host cannot be controlled, and that includes starting a session from the web app.

Running the work in the cloud means someone else keeps it up. Keeping it local means your files, credentials and hardware stay yours, and you are responsible for uptime. Every number in this post (the 500 ms to 10 s backoff, the five-minute screen refresh, the 12-hour and 30-day lifetimes) is in the MIT-licensed shell.online repository, so you can check them rather than take our word for it.

Check on a long run from your phone

Install the CLI, start your agent or job with shell, and open the printed link anywhere. If the network drops, the session reconnects on its own, and while the machine is away the viewer shows its last screen.

Try shell.online
About this article

Published by the Pilot Protocol team. Product claims are scoped to the availability labels and technical references linked in the article; deployment behavior can vary by version and environment.

How we publish · Suggest a correction · Technical references