The normal case
handled by supervisorA tmux session whose foreground process is a small supervisor loop. It launched claude and is watching it. The conversation is pinned to a fixed uuid, and its history is written to a jsonl transcript.
[ open source · MIT ]
Persistent, self-healing Claude Code sessions in tmux
A single daemon per machine keeps a fleet of long-running agent sessions alive: it heals crashed ones, brings them back on reboot, and resumes the same conversation by a pinned uuid. Sessions are full interactive claude processes — ccmux supervises them, it does not reimplement them.
curl -fsSL https://github.com/max-listov/ccmux/releases/latest/download/install.sh | bashRequires macOS (launchd) or Linux (systemd), plus tmux.
Pick a command to see what it actually prints. These are real outputs, taken from a running install — not mock-ups.
SESSION MODEL CTX STATE UPTIME RC DIR cc-api Opus 5 890.0k/1.0M 89% working 16m prod-api ~/code/api cc-web Opus 5 420.0k/1.0M 42% idle 3d11h prod-web ~/code/web cc-infra Opus 5 740.0k/1.0M 74% idle 3d20h prod-infra ~/infra cc-docs Opus 4.8 100.0k/1.0M 10% waiting 3d20h prod-docs ~/code/docs
Context left, state and uptime per session. Note the uptime in days — these survive reboots.
added: cc-api|/root/code/api|27e0665f-5ecc-4e67-b840-f32c49f2c791 started cc-api (prod-api)
A fixed uuid is pinned right away — every later resume uses it, so there is no session picker to get stuck on.
sent to cc-api: /compact
Text or a slash command lands in the pane as if typed by hand. This is what makes scripts and hooks possible.
restarting cc-api stopped cc-api started cc-api (prod-api) — resumed 27e0665f
Survives killing the caller — an agent can restart itself and come back to the same conversation.
{"seq":841,"role":"user","kind":"text","createdAt":"2026-07-28T21:14:03Z",
"text":"check why the deploy failed"}
{"seq":842,"role":"assistant","kind":"tool_use","name":"Bash",
"input":{"command":"journalctl -u api -n 50"},
"resultText":"Jul 28 21:12:44 api[1180]: FATAL: password authentication failed"}Structured messages with tool calls and their results, read from the jsonl transcript — not scraped off the screen.
ccmux 0.1.19 self: /root/.bun/bin/bun /root/.ccmux/app/ccmux.js config: /root/.config/ccmux/machine.json sessions: /root/.ccmux-sessions rc prefix: prod boot label: ccmux.service claude: /root/.local/bin/claude (ok) codex: /usr/bin/codex (ok) tmux: /usr/bin/tmux (ok) daemon: active (systemd)
The first thing to ask when a machine's fleet behaves oddly.
queued for cc-web (deferred until it finishes its turn)
Hand work from one agent to another without relaying it yourself. Off by default on both ends.
Six situations a long-running agent session actually meets. Pick one to see what happens and who handles it.
A tmux session whose foreground process is a small supervisor loop. It launched claude and is watching it. The conversation is pinned to a fixed uuid, and its history is written to a jsonl transcript.
The supervisor notices the process is gone and relaunches it with exponential backoff, resuming the pinned uuid. From the outside it looks like a pause, not a loss. Killing the tmux session is the only way to actually stop it.
One daemon per machine is registered with launchd or systemd. On boot it re-creates every managed session; between boots it re-checks the fleet every 30 seconds and heals whatever is missing.
Claude itself does not keep a uuid forever: running out of context forks the conversation into a new session id with a new jsonl. Each heal pass detects that the conversation moved and re-pins the registry, so transcripts and the next restart follow the live conversation instead of a dead file.
Large or old sessions get a blocking "resume from summary?" prompt. An unattended heal would strand at that menu, and input would land on the menu instead of the conversation. The supervisor answers it automatically according to the machine config.
An explicit stop or rm ends the session — the daemon does not resurrect it. The jsonl history stays on disk, so a removed session can be brought back later with adopt by its uuid.
A machine default plus an optional per-session override. Under a root daemon, escalated modes are downgraded at launch — a config edit cannot hand a server session host-wide power.
Opt-in messaging between managed sessions, so one agent hands off to another without you relaying. Messages can be deferred until the recipient finishes its turn, or armed on a timer.
A claude you started by hand shows up as external. Fork it into a safe copy, or take over the original.
Releases are born only from tags, in CI. Machines pick up the verified bundle on their own — and sessions survive the update.
It comes back. The foreground process of each tmux session is a small supervisor loop that relaunches the agent with exponential backoff, so a crash is just a pause. Killing the tmux session is the only way to actually stop it.
Yes. One daemon per machine is registered with launchd or systemd, starts on boot and re-creates every managed session within about thirty seconds.
No. Every session pins a fixed uuid — created once, resumed after — so a restart continues the same conversation instead of opening a new one. There is no session picker to get stuck on, and blocking resume prompts are answered automatically so an unattended heal never stalls at a menu.
When Claude runs out of context it forks the conversation into a new session id, leaving the old transcript behind. Each heal pass detects that the conversation moved and re-pins the registry, so transcripts and the next restart follow the live conversation rather than a dead file.
There is a root guard: under a root daemon, escalated permission modes are downgraded to auto at launch — whether they came from the machine default or a per-session override. A config edit cannot hand a server session host-wide power.
Yes, and it is off by default. Enable chat per session, then one session can message another; messages can be deferred until the recipient voluntarily finishes its turn, or armed on a timer. Nothing is sent or received until both ends opt in.
Yes. Sessions live on the default tmux socket, so tmux attach, tmux ls and detaching with Ctrl-b d all work as usual. ccmux supervises them; it does not hide them behind a custom protocol.