File Undo (otter /undo and /redo)
Otter’s /undo rewinds the conversation and any files the agent touched in that turn — not just the message history. It works in any directory, with or without git, by maintaining a content-addressed file snapshot store keyed per session.
How /undo and /redo work
Section titled “How /undo and /redo work”After every assistant turn, the otter REPL takes a snapshot:
- Checkpoint — a
chimera.checkpoints.CheckpointManagersnapshots the workspace and pushes aCheckpointInfoonto the per-session undo stack. - Conversation snapshot — the session’s
Contextmessages are deep-copied so the conversation can be rolled back alongside the filesystem. - File shadow — the on-disk
FileSnapshotStorerecords every file the agent’sFileTrackerreports as modified, content-addressed by SHA-256. - Redo invalidation — any pending redo entries are dropped (a fresh turn invalidates the redo path).
/undo pops the top of the undo stack, pushes it onto the redo stack, then restores the resulting top-of-undo (or the initial state if the stack is empty). /redo is the inverse: pop redo, restore, push back onto undo.
On-disk layout
Section titled “On-disk layout”~/.chimera/snapshots/<session-id>/ blobs/<sha256> # content-addressed file payloads snaps/<snap-id>/manifest.json # {abs_path: sha256 | null}Each per-turn manifest records every file modified at any point during the session, content-addressed via SHA-256 so unchanged files share a single blob. Files that did not exist at snap time are recorded as null so restore knows to delete them on rewind (matching git checkout semantics).
The store deliberately uses plain file copies rather than shelling out to git: otter sessions live in arbitrary working directories that may or may not be a git repo.
Sentinel limit
Section titled “Sentinel limit”Files larger than 25 MiB are not snapped (we record null instead) so a runaway log file doesn’t balloon ~/.chimera. Symlinks are followed.
Prerequisites
Section titled “Prerequisites”- Chimera installed:
pip install chimera-run - An otter session (e.g.
otter chat) - Write access to
~/.chimera/snapshots/(override with$CHIMERA_SNAPSHOT_ROOT)
Slash commands
Section titled “Slash commands”| Command | Effect |
|---|---|
/undo | Roll the conversation and modified files back to the previous turn. |
/redo | Re-apply the most recently undone turn. |
/new | Discard the session, including its snapshot shadow. |
/undo is idempotent at the boundary: when the undo stack is empty, restoring the initial state is a no-op. The redo stack tracks however many /undos have happened in a row.
Python API
Section titled “Python API”from chimera.otter.snapshot import FileSnapshotStore
store = FileSnapshotStore(session_id="my-session")
# Snap the current state of files the agent modifiedsnap = store.snap(["/abs/path/a.py", "/abs/path/b.py"])
# Later, restore that snaprestored_paths = store.restore(snap.snap_id)
# Housekeepingstore.discard("snap-1-...") # drop a single snapstore.gc_blobs() # reclaim space; returns count removedstore.clear() # wipe the whole shadow for this sessionOverride the snapshot root
Section titled “Override the snapshot root”Set CHIMERA_SNAPSHOT_ROOT to redirect the shadow under a tmp dir (used by tests, by sandboxed CI, or by isolated harnesses):
export CHIMERA_SNAPSHOT_ROOT=/tmp/chimera-snapshotsWorked example
Section titled “Worked example”otter> Read main.py and refactor the load() function.... agent runs, edits main.py and tests/test_main.py ...
otter> /undo[ok] rolled back 2 file(s); messages restored.
otter> /redo[ok] re-applied; main.py and tests/test_main.py back to post-turn state.
otter> /new[ok] session cleared; snapshot shadow wiped.Storage cost
Section titled “Storage cost”Because the shadow is content-addressed, unchanged files across N turns cost O(1) storage (one shared blob), not O(N). gc_blobs() walks every manifest, builds the live-blob set, and unlinks orphans.
See also
Section titled “See also”- Hook events —
FileChangedandCwdChangedfire on tracked-file edits and working-directory changes. - Subagent profiles —
executor+reviewerflow benefits from/undoafter a regrettable change.