Add autosave-on-exit and /resume for last-session continuation
July 11, 2026 · View on GitHub
Summary
This change adds a lightweight autosave + resume flow so users can continue their latest REPL session without manually selecting a file.
What was added
-
New autosave function:
save_latest(args, state, _config)- File:
cheetahclaws.py - Saves to:
MR_SESSION_DIR / "session_latest.json" - Ensures parent directory exists:
path.parent.mkdir(parents=True, exist_ok=True)
- Persists:
messagesturn_counttotal_input_tokenstotal_output_tokens
- File:
-
New resume function:
cmd_resume(args, state, _config)- File:
cheetahclaws.py /resumewith no args loads:MR_SESSION_DIR / "session_latest.json"
/resume <file>loads:MR_SESSION_DIR / <file>(or direct path if/present)
- Restores state in same style as
/load.
- File:
-
Slash command registration:
- Added
"resume": cmd_resumeinCOMMANDS.
- Added
REPL exit behavior
Autosave now runs on abrupt prompt exit in the main REPL loop:
- On
EOFError/KeyboardInterruptwhile waiting for input:- Calls
save_latest("", state, config) - Then exits cleanly.
- Calls
Also on explicit command exit:
/exitand/quit(cmd_exit) callsave_latest("", _state, _config)beforesys.exit(0).
Slash dispatch flow
handle_slash() now treats known slash commands as handled and returns True, preventing /resume from falling through into normal run_query(...) chat execution.
User flow
- Use the agent normally.
- Exit via
/exit,Ctrl+C, orCtrl+Dat prompt. - Session autosaves to
mr_sessions/session_latest.json. - Restart agent and run
/resume. - Continue from restored conversation state.
Update — per-turn crash-safe autosave
The original flow above only wrote session_latest.json on exit (clean quit,
Ctrl+C/Ctrl+D, or when a budget cap was hit). A power-loss or hard kill
mid-conversation therefore lost everything since the session started.
autosave_session(state, config) (in commands/session.py) closes that gap:
- Called from
run_queryat the end of every turn (right after the per-turn checkpoint snapshot), so the on-disk transcript is never more than one turn stale. - Only rewrites
session_latest.json. It deliberately does not write adaily/copy, append tohistory.json, touch SQLite, or print — those remain exit-time finalization steps insave_latest(). Doing them every turn would spamdaily/andhistory.jsonwith dozens of partial sessions. - Durable + atomic: writes to a temp file,
flush()+os.fsync()to force bytes to disk (survives a power cut), thenos.replace()for an atomic swap — a crash can never leave a half-writtensession_latest.json. - Best-effort: wrapped so it can never raise into the REPL loop, and reuses
one stable
session_idfor the running session so each turn overwrites the same file instead of churning new ids.
Net effect: /resume now recovers a conversation after a crash or power-loss,
not just after a clean exit. File edits (Write/Edit tools) were already written
to disk immediately, and explicit /remember writes are already immediate, so
those were never at risk — the transcript was the only gap.