Local crash recovery for Claude Code and Codex sessions running inside CMUX.
CMUX can restore workspace layout after a relaunch, app update, macOS reboot, or crash, but the live Claude Code/Codex processes are gone. This tool records enough local metadata to resume the right agent session in the right restored workspace.
- Save session metadata automatically on every agent turn.
- After CMUX restarts, recover one workspace at a time with
cmr. - Never start a fresh
claudeorcodexsession when a resume ID is missing. - Never bulk-start every old agent after a crash.
- Ask before resuming when a restored title points at several title-only matches.
- Log per-workspace memory and CPU telemetry so runaway sessions are easier to find.
- Safely stop older/heavy agents while keeping the CMUX tabs ready to recover later.
This is a stopgap, not a replacement for native CMUX session persistence. It does not keep killed processes alive. It makes Claude Code/Codex recovery fast and explicit using their own resume mechanisms.
git clone https://github.com/Lumiwealth/cmux-agent-recovery.git
cd cmux-agent-recoveryAdd the repo's bin/ directory to your PATH, then wire hooks/agent-record.sh into Claude Code and Codex hook events.
The important hook command shape is:
/path/to/cmux-agent-recovery/hooks/agent-record.sh claude UserPromptSubmit
/path/to/cmux-agent-recovery/hooks/agent-record.sh codex UserPromptSubmitOptional short command:
cmr() {
"/path/to/cmux-agent-recovery/bin/cmr" "$@"
}Then, inside a restored CMUX workspace:
cmrcmrRecover the current CMUX workspace. If there is one safe match, it starts:
cd "$HOME/Development" && claude --resume <session-id>
cd "$HOME/Development" && codex -C "$HOME/Development" resume <session-id>If multiple plausible sessions match, cmr prints choices with the workspace name:
cmr: multiple possible sessions found for workspace 'Release Notes'. Pick one with `cmr N`:
1. codex score=100 last=2026-04-30T01:00:00Z title='Release Notes' cwd=/work/repo resume_cwd=/work/repo transcript=12.4MB
2. codex score=90 last=2026-04-27T10:00:00Z title='Release Notes' cwd=/work/old resume_cwd=/work/old transcript=1.1MB
Then run:
cmr 1If a tab title was reused or the DB got polluted by a wrong resume, pin the title to the known-good session:
cmux-recovery pin --title "Release Notes" --tool codex --session-id <session-id> --cwd "$HOME/Development"Bad matches can be hidden:
cmux-recovery ignore --tool claude --session-id <wrong-session-id> --reason "wrong workspace"Dry run:
cmr --dry-runDry run uses the same match selection as normal recovery. It only prints the command instead of executing it.
If a Codex transcript is extremely large, cmr prints the transcript size/path and still launches Codex by default. Large transcripts can still be killed while Codex loads the history, but normal cmr does not require a separate force flag. The default warning cutoff is 4 GB; cmr --block-large-transcript is available when intentionally testing the guard without launching Codex.
Exact-title matches can still be rejected when the transcript clearly belongs to a generic workflow and has no independent match to the requested tab topic, screen, workspace id, or cwd. This prevents renamed tabs from offering stale LinkedIn workflow sessions as recoverable choices.
When a crash happens before a Codex tab gets a real CMUX title, the restored tab may only be named Development. In that case cmr also lists recent untitled Codex state entries with the same cwd as recent-cwd candidates. A clearly newest recent-cwd candidate auto-selects when it is less than two hours old and at least ten minutes newer than the next recent-cwd candidate; otherwise cmr still requires an explicit choice.
For Claude Code, cmr prefers the project directory that owns the Claude transcript when it can prove that path. That matters when a session used cd during the conversation: Claude may store the resumable session under the original project even if the latest hook saw a child folder.
Audit all current CMUX workspaces:
cmux-recovery verifyRecord and inspect workspace memory and CPU usage:
cmux-recovery memory-snapshot
cmux-recovery memory-list
cmux-recovery memory-top --since 6hShow memory and CPU directly in the CMUX sidebar by writing a managed line to each workspace description:
cmux-recovery sidebar-metrics
cmux-recovery sidebar-metrics --execute
cmux-recovery sidebar-metrics --clear --executeThis does not change workspace titles. It preserves any existing description text except prior managed cmux: metric lines.
Plan a safe memory trim:
cmux-recovery trimtrim samples current workspace memory, finds older workspaces with a safe Claude/Codex resume ID, skips the active workspace by default, and prints what it would stop. To actually stop those agents while leaving the CMUX tabs in place:
cmux-recovery trim --executeAfter a tab is stopped, run cmr inside that tab to resume it.
Check total pressure before macOS gets into swap collapse:
cmux-recovery pressurepressure combines live CMUX agent memory, physical RAM, swap usage, and free disk space. It returns non-zero when pressure is warning or critical and includes the same dry-run trim plan as trim.
Install a per-minute local LaunchAgent that sends a macOS notification on warning or critical pressure:
bin/install-pressure-monitorNotifications are throttled to once every 5 minutes while the pressure level stays the same. Warning starts at about 40 GB of monitored agent memory. Critical starts at about 50 GB. The alert treats about 60 GB as the crash danger zone, dedupes duplicate CMUX tab mappings that point at the same TTY/process tree, and shows a short plain-language summary with the biggest tabs or background processes to close/restart.
Remove it:
bin/uninstall-pressure-monitorImport old recovery reports and local agent state:
cmux-recovery import-legacy
cmux-recovery import-legacy --report-dir /path/to/cmux/reportsThe live database and runtime exports are ignored by git:
state/cmux-recovery.sqlite
state/latest.json
logs/
reports/
exports/
The database stores:
- CMUX workspace title and normalized title
- workspace/surface IDs and refs when available
- workspace order/index
- current directory and terminal/process title
- Claude/Codex session ID
- transcript or rollout path
- tool, model, permission/sandbox mode where available
- first seen, last seen, and restore attempts
- per-workspace telemetry samples: approximate live agent memory,
%MEM,%CPU, process count, and top process names
Treat the database as private local state. Session IDs, transcript paths, and workspace titles can reveal sensitive project context.
cmrnever runs plainclaude.cmrnever runs plaincodex.- Ambiguous matches require an explicit number.
- Missing matches do nothing.
- Workspace titles are matched literally and are not parsed for tool metadata.
- Restore is one workspace at a time by default.
trimonly stops workspaces with a recoverable session ID, skips the active workspace by default, and runs as a dry run unless--executeis passed.
Typical local hook wiring lives in:
~/.claude/settings.json~/.codex/hooks.json~/.zshrc
Hook formats change over time, so keep the installed hook small: pass the tool name and event into hooks/agent-record.sh, and keep hook stdout silent.
CMUX currently restores layout and metadata after relaunch, but it does not restore live process state. This tool fills that gap for Claude Code and Codex by storing resume metadata outside CMUX.
- Stable agent session recovery after CMUX crash or relaunch
- Persist session manifest and re-attach live agent processes after daemon/app restart
- Show per-workspace/session memory and CPU usage in sidebar
- Native memory telemetry CLI and trim recoverable agent command
- Fix 80GB memory growth from retained WebKit and cmux monitor helpers