feat(sessions): /quit or 'stop' parks a session instead of respawning it - #213
Merged
Conversation
…es it /quit (and any exit-0 agent exit) used to be respawned-and-resumed within ~2s, and nothing short of rm/delete could close a session for good. A stopped:true flag in sessions.json now parks a session — still listed, deliberately down: set by the pane epilogue on a clean agent exit and by the new 'agent-box-session stop NAME'; cleared by restart (CLI or settings page), which becomes the revive verb. Crashes keep the post-mortem bash, kills/reboots/Spot stops keep respawn-and-resume, and codex RC daemon + shell sessions keep their old semantics (a dying RC daemon exits 0 and must self-heal, not park). Closes #167 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SDHb5tyTqZwzKM6Qdr8cxv
lionello
approved these changes
Aug 12, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #167.
Motivation
Sessions respawn automatically whenever their tmux session dies — the right behavior for a Spot cycle, an OS reboot, or a crash, but it also swallowed every intentional exit: typing
/quitinto a claude session killed the TUI, and ~2s later the supervisor respawned it with--resume. There was no way for a client (Remote Control, the CLI, an agent managing its own helpers) to close a session for good short of the web UI's delete oragent-box-session rm, which throw the session's config away.What changed
A session entry in
sessions.jsoncan now carrystopped: true— parked: still listed, deliberately left down. Three things set/clear it:agent && mark-stopped || exec bash: exit 0 (/quit, Ctrl+D — an exit somebody asked for) recordsstopped: truebefore the pane ends. A non-zero exit still gets the post-mortem inspection bash, and a kill (restart, reboot, Spot stop) runs no epilogue — those still respawn and resume, unchanged. So/quitnow does what it says, from the terminal or from a Remote Control client.agent-box-session stop NAMEparks a session imperatively (flag first, then kill, so the supervisor's post-spawn re-check catches the race the same wayrm's delist-then-kill does).restart NAMEis the revive verb: it clears the flag and the supervisor brings the session back (resuming its transcript as before).restart --allrevives everything. The settings page's Restart button does the same.The supervisor's reconcile loop skips stopped entries; its pre-/post-spawn delist re-check now also covers the stopped flag.
Two session kinds opt out of park-on-clean-exit:
stopinstead.mark-stoppedis a verified write with retries: on an agent that exits within its first seconds, the supervisor'smark_startedrewrite can race it (both are tmp+mv), so it re-reads until the flag stuck, and never re-creates a session delisted meanwhile.User-visible effects
/quit(or any clean agent exit) closes the session instead of resuming it.agent-box-session stop NAME;lsshows astoppedstate;restartrevives (and now tolerates a session with nothing live to kill instead of erroring).stoppedbadge; the workspace pane placeholder says the session is stopped rather than promising it is "starting…";session_countsexcludes stopped sessions so the "Restart all" spinner can't hang on one.No security-relevant surface changes: the new
mark-stoppedhelper writes only the user's ownsessions.json(same tmp+mv discipline as the existing writers), and the session name it receives isprintf %q-quoted into the pane command like every other runtime field.Checks run
nix run .#assemble+module-generated-up-to-date(aarch64-linux)nix run .#update-golden+golden-snapshot— fixture diff is the supervisor/session-CLI/settings payloads plus the newmark-stoppedpayload per usermulti-user,module-single-file,download-route,webhook-route(aarch64-linux)tests/sessions.nixextended (runs in CI, x86_64-only): clean exit parks (claude --helpas the exit-0 stand-in),stopparks,restartrevives and clears the flagclaude --dangerously-skip-permissions --remote-control x --helpexits 0🤖 Generated with Claude Code
https://claude.ai/code/session_01SDHb5tyTqZwzKM6Qdr8cxv