HomeBlog › Killing the session shouldn't kill the conversation
Blog

Killing the session shouldn't kill the conversation

Aug 17, 20264 min readBy the Rysh team

A pane that reappears proves nothing. The question is whether the conversation came back with it.

If you run coding agents in a terminal multiplexer, detaching is the easy case. The daemon owns the PTYs, so closing your laptop lid drops the connection and everything keeps running. Every multiplexer since screen has done that.

Stopping the session is the hard case. The daemon exits, every pane shell is killed, and the agents go with it. When you start it again you get your layout back — tabs, lanes, panes, scrollback — and five dead terminals where your agents used to be. The shape survived. The work didn't.

Here is that gap being closed, in two minutes:

If the embed does not load, it is here on YouTube.

The test is a codeword, not a screenshot

The demo does one thing deliberately. Before anything is stopped, each agent is told a codeword and nothing else — one gets WALRUS-1111, the other MARMOT-2222. Nothing on disk in the project mentions either word.

Then rysh stop takes the daemon down and both agent processes with it. On the next attach, each pane comes back and is asked what codeword it was given. Both answer correctly.

That distinction is the whole point. A pane that merely reappears proves nothing — it is easy to respawn a shell in the right place and call it persistence. The codeword is the only evidence that the conversation came back, because the answer cannot be reconstructed from anything left in the working directory.

How each half actually works

The two agents need completely different mechanics, because the two CLIs make different promises about session identity.

Claude pins its id at launch. ##claude starts claude --session-id <pane-uuid>, so the conversation id is the pane id. Restoring it is then a matter of replaying claude --resume <pane-uuid> in the same directory. The pane id is the invariant everything else hangs off — which is also why ##claude new (a fresh conversation under a new id) and ##claude --fresh-session (start over, keep the pane id) are different commands.

Codex issues its own id, so it has to be discovered. There is no launch-time flag to pin one. rysh watches ~/.codex/sessions/ for a rollout-*.jsonl whose first line's cwd matches the pane's recorded directory and whose mtime is at or after launch. That id is stamped into the pane's metadata, and the restore replays codex resume <id>.

The awkward case is two codex panes in the same directory — exactly what codex resume --last cannot resolve. Each gets its own discovered id, so each comes back to its own conversation.

On restart, a sweep walks the panes that rysh itself launched agents in, and replays each resume line behind a foreground probe. Two design choices worth naming:

What this does not claim

Persistence stories are usually sold with the caveats removed. These are the ones that matter, and they are in the design doc rather than a footnote:

Building this also turned up two defects that no unit test could have produced. Codex's cwd comparison was a string compare, so /tmp/x never matched /private/tmp/x and every codex pane on a Mac would have silently fallen back to --last. And a pane that lost its metadata could never start an agent again, because re-pinning its own id makes claude exit with Session ID … is already in use. Both were fixed with regressions written red first.

Try it

rysh is open source, Apache-2.0, at github.com/rysh-ai/rysh-cli-code — everything above is in the public tags v0.2.6, v0.2.7 and v0.2.9.

go install github.com/rysh-ai/rysh-cli-code/cmd/rysh@latest
rysh create work        # a session is a daemon, not your terminal
##claude                # or ##codex — runs in this pane, pinned to it
rysh stop work
rysh attach work        # the agents come back on their own

Tell one a codeword first. It is a better test than watching the panes redraw.

Try Rysh

Every pane is a shell and an AI agent — install takes one command.

Get started free →