Контрольные точки и /rollback
Перед деструктивными операциями Hermes Agent умеет автоматически снимать слепок проекта, а потом восстанавливать его одной командой. Начиная с v2 контрольные точки работают по принципу opt-in: их нужно включить вручную. Большинство пользователей /rollback так и не трогают, а теневое хранилище со временем разрастается до заметных размеров — поэтому по умолчанию функция выключена.
Включить контрольные точки для одной сессии можно флагом --checkpoints:
hermes chat --checkpoints
Либо глобально — в ~/.hermes/config.yaml:
checkpoints:
enabled: true
За эту страховку отвечает внутренний Checkpoint Manager. Он держит единый общий теневой git-репозиторий в каталоге ~/.hermes/checkpoints/store/, а настоящий .git вашего проекта при этом остаётся нетронутым. Все проекты, с которыми работает агент, используют одно и то же хранилище — благодаря content-addressable базе объектов git дедупликация идёт сразу и между проектами, и между ходами диалога.
Что создаёт контрольную точку
Слепок создаётся автоматически перед такими действиями:
- Файловые инструменты —
write_fileиpatch - Деструктивные команды терминала —
rm,rmdir,cp,install,mv,sed -i,truncate,dd,shred, перенаправление вывода (>), а такжеgit reset/clean/checkout
За один ход агент делает не более одной контрольной точки на каталог, так что долгие сессии не засыпают вас лавиной снимков.
Краткая справка
Слеш-команды внутри сессии:
| Команда | Описание |
|---|---|
/rollback | Список всех контрольных точек со статистикой изменений |
/rollback <N> | Восстановить состояние из точки N, сохранив ваши ручные правки (заодно отменяет последний ход чата) |
/rollback <N> --all | Полное восстановление — затирает и ваши ручные правки тоже |
/rollback diff <N> | Показать diff между точкой N и текущим состоянием |
/rollback <N> <file> | Восстановить один файл из точки N |
CLI для просмотра и обслуживания хранилища вне сессии:
| Команда | Описание |
|---|---|
hermes checkpoints | Общий размер, число проектов, разбивка по проектам |
hermes checkpoints status | То же, что и просто checkpoints |
hermes checkpoints list | Псевдоним для status |
hermes checkpoints prune | Принудительная чистка: удаление сирот и устаревших записей, GC, контроль лимита по размеру |
hermes checkpoints clear | Снести всё хранилище контрольных точек (сначала спросит) |
hermes checkpoints clear-legacy | Удалить только архивы legacy-*, оставшиеся после миграции с v1 |
Как работают контрольные точки
Если коротко:
- Hermes отслеживает момент, когда инструменты вот-вот изменят файлы в вашем рабочем дереве.
- Раз за ход диалога (отдельно для каждого каталога) происходит следующее:
- Определяется разумный корень проекта для файла.
- Инициализируется или переиспользуется единое общее теневое хранилище в
~/.hermes/checkpoints/store/. - Изменения добавляются в индекс конкретного проекта, строится дерево, а коммит уходит в проектный ref (
refs/hermes/<project-hash>).
- Эти проектные ref-ы и образуют историю контрольных точек, которую можно просматривать и восстанавливать через
/rollback.
flowchart LR
user["User command\n(hermes, gateway)"]
agent["AIAgent\n(run_agent.py)"]
tools["File & terminal tools"]
cpMgr["CheckpointManager"]
store["Shared shadow store\n~/.hermes/checkpoints/store/"]
user --> agent
agent -->|"tool call"| tools
tools -->|"before mutate\nensure_checkpoint()"| cpMgr
cpMgr -->|"git add/commit-tree/update-ref"| store
cpMgr -->|"OK / skipped"| tools
tools -->|"apply changes"| agent
Настройка
Параметры задаются в ~/.hermes/config.yaml:
checkpoints:
enabled: false # главный переключатель (по умолчанию: false — opt-in)
max_snapshots: 20 # максимум контрольных точек на проект (через переписывание ref + gc)
max_total_size_mb: 500 # жёсткий лимит на общий размер хранилища; старейшие коммиты удаляются
max_file_size_mb: 10 # пропускать любой файл крупнее этого размера
# Автообслуживание (включено по умолчанию): чистка ~/.hermes/checkpoints/
# в фоне — CLI делает её в вспомогательном потоке сразу после запуска, шлюз —
# на своём тике обслуживания. Удаляются записи проектов, чьё last_touch
# старше retention_days. Запускается не чаще одного раза в min_interval_hours,
# отслеживается маркером .last_prune. Ни приглашение ввода, ни старт шлюза
# она не блокирует: `git gc`, который освобождает место, на большом хранилище
# может идти десятки секунд. Эта чистка никогда не удаляет «осиротевшие» записи
# (рабочий каталог не найден) — отсутствие каталога неоднозначно:
# это может быть удалённый проект, а может — неподключённый внешний диск,
# сетевая шара или ещё не поднявшийся VPN. Поэтому чистка сирот выполняется
# только явной командой `hermes checkpoints prune` (см. ниже), с запросом
# подтверждения.
auto_prune: true
retention_days: 7
min_interval_hours: 24
Чтобы отключить всё целиком:
checkpoints:
enabled: false
auto_prune: false
При enabled: false Checkpoint Manager превращается в no-op и не выполняет ни одной git-операции. При auto_prune: false хранилище растёт, пока вы вручную не запустите hermes checkpoints prune.
Список контрольных точек
Из сессии CLI:
/rollback
Hermes выдаст отформатированный список со статистикой изменений:
📸 Checkpoints for /path/to/project:
1. 4270a8c 2026-03-16 04:36 before patch (1 file, +1/-0)
2. eaf4c1f 2026-03-16 04:35 before write_file
3. b3f9d2e 2026-03-16 04:34 before terminal: sed -i s/old/new/ config.py (1 file, +1/-1)
/rollback <N> restore to checkpoint N (keeps your hand-edits)
/rollback <N> --all full restore, overwriting your hand-edits too
/rollback diff <N> preview changes since checkpoint N
/rollback <N> <file> restore a single file from checkpoint N
Просмотр хранилища из шелла
hermes checkpoints
Пример вывода:
Checkpoint base: /home/you/.hermes/checkpoints
Total size: 142.3 MB
store/ 138.1 MB
legacy-* 4.2 MB
Projects: 12
WORKDIR COMMITS LAST TOUCH STATE
/home/you/code/hermes-agent 20 2h ago live
/home/you/code/experiments/rl-runner 8 1d ago live
/home/you/code/old-prototype 3 9d ago orphan
...
Legacy archives (1):
legacy-20260506-050616 4.2 MB
Clear with: hermes checkpoints clear-legacy
Принудительная полная чистка (игнорирует 24-часовой маркер идемпотентности):
hermes checkpoints prune --retention-days 3 --max-size-mb 200
Предпросмотр изменений через /rollback diff
Прежде чем соглашаться на восстановление, посмотрите, что вообще поменялось с момента контрольной точки:
/rollback diff 1
Команда покажет сводку git diff stat, а следом — сам diff.
Восстановление через /rollback
/rollback 1
Под капотом Hermes делает следующее:
- Проверяет, что нужный коммит действительно есть в теневом хранилище.
- Снимает слепок состояния перед откатом — чтобы позже можно было «отменить отмену».
- Восстанавливает отслеживаемые файлы в рабочем каталоге — сохраняя ваши ручные правки (см. ниже).
- Отменяет последний ход диалога, чтобы контекст агента совпадал с восстановленным состоянием файлов.
Ручные правки сохраняются по умолчанию
/rollback <N> восстанавливает только те файлы, которые менял сам Hermes. Каждый успешный write_file / patch записывает хеш содержимого файла в журнал правок агента (agent-write ledger); при восстановлении файл, чьё текущее содержимое уже не совпадает с тем, что последним записал Hermes (вы правили его после этого, либо Hermes вообще его не трогал), пропускается вместо перезаписи и попадает в отдельный список в выводе:
✅ Restored to checkpoint a1b2c3d4: before write_file
↷ Kept your hand-edits: src/config.py, notes.md
Use /rollback <N> --all to restore those too.
Чтобы принудительно выполнить классическое полное восстановление, откатывающее всё — включая ваши собственные правки, — добавьте --all:
/rollback 1 --all
Если журнал пуст (хранилище создано до появления этой функции, либо Hermes ещё не записывал файлы в проекте), /rollback автоматически откатывается к полному восстановлению.
Восстановление одного файла
Можно вернуть лишь один файл из контрольной точки, не затрагивая остальной каталог:
/rollback 1 src/broken_file.py
Защита и ограничители производительности
Вложенные Git-репозитории
Контрольная точка родительской директории может сохранить вложенный репозиторий как Git-gitlink (ссылку на коммит), а не как копию его файлов. Рекурсивный захват вложенных репозиториев не поддерживается: их незакоммиченные правки и неотслеживаемые файлы из такой родительской контрольной точки не восстановить. Контрольные точки, созданные этой или более поздней версией, помечаются в списке /rollback, например: before write_file: app.py [nested git repos not captured: tool].
Если выбранная контрольная точка содержит gitlink, полный откат (включая --all) отклоняется ещё до изменения файлов и до создания снимка перед откатом. Восстановление вложенного репозитория, файла внутри него или Git-pathspec, под который он подходит, тоже отклоняется: Hermes не станет рапортовать об успехе для файлов, которые не были захвачены. Несвязанные захваченные файлы по-прежнему можно восстановить, например /rollback 1 notes.txt. Храните отдельные резервные копии или контрольные точки, снятые прямо из рабочей директории вложенного репозитория.
Контейнерные бэкенды
При контейнерном бэкенде терминала (docker, singularity, modal, daytona, vercel_sandbox или контейнерный плагин) пути к файлам относятся к песочнице, а не к хосту. Поэтому Hermes не создаёт для таких путей контрольные точки и не ведёт журнал записей агента, а /rollback объясняет это ограничение: существующие контрольные точки хоста он по-прежнему выводит списком, но diff и восстановление отклоняет — и в CLI, и в чатах через шлюз мессенджеров. /diff session отвечает с той же причиной. TUI и десктоп ведут себя так же: /rollback list работает, а /rollback diff и /rollback <N> отклоняются с этой причиной. Локального и SSH-бэкенда это не касается. Как направить terminal.cwd на вид смонтированного каталога изнутри контейнера, описано в terminal.docker_mount_cwd_to_workspace.
- Наличие git — если
gitне найден вPATH, контрольные точки молча отключаются. - Область каталога — Hermes пропускает слишком широкие каталоги (корень
/, домашний$HOME). - Размер репозитория — каталоги, где больше 50 000 файлов, пропускаются.
- Лимит на размер файла — файлы крупнее
max_file_size_mb(по умолчанию 10 МБ) в снимок не попадают. Так в хранилище случайно не утекут датасеты, веса модели или сгенерированная медиатека. - Лимит на общий размер хранилища — когда оно перерастает
max_total_size_mb(по умолчанию 500 МБ), самый старый коммит каждого проекта удаляется по кругу. После каждого удаления место сначала освобождается, и только потом решается, нужно ли удалять ещё. У каждого проекта остаётся хотя бы один снимок. Неудачная git-операция останавливает чистку, и об этом сообщается; обслуживание никогда не удаляет больше истории, чтобы компенсировать неудавшееся освобождение места. - Реальная чистка, в стороне от горячего пути —
max_snapshotsи лимит размера соблюдаются переписыванием проектного ref в момент контрольной точки (дёшево); затем хранилище помечается.gc-pending, и периодическая чистка один раз запускаетgit gc --prune=now, так что мусорные объекты не накапливаются, а вызов инструмента никогда не ждёт полной перепаковки. - Параллельные операции — снимки, восстановления, диффы и обслуживание используют одну общую для процессов блокировку хранилища. Операция сообщает, что хранилище занято, а не запускает GC поверх неопубликованных объектов другого процесса. Восстановление сначала применяет выбранное дерево и только потом подрезает историю страховочного снимка.
- Снимки без изменений — если с момента прошлого снимка ничего не поменялось, контрольная точка не создаётся.
- Нефатальные ошибки — сбои снимков не блокируют ваши инструменты. Сбои чистки пишутся в лог как предупреждения; явное обслуживание сообщает число ошибок.
Где живут контрольные точки
~/.hermes/checkpoints/
├── store/ # единый общий bare git-репозиторий
│ ├── HEAD, objects/ # внутренности git (общие для всех проектов)
│ ├── refs/hermes/<hash> # вершина ветки конкретного проекта
│ ├── indexes/<hash> # git-индекс конкретного проекта
│ ├── projects/<hash>.json # workdir + created_at + last_touch
│ ├── .gc-pending # ref переписаны после последнего gc; сбрасывается следующей чисткой
│ └── info/exclude
├── .last_prune # маркер идемпотентности авточистки
└── legacy-<ts>/ # архивы теневых репозиториев проектов из эпохи pre-v2
Каждый <hash> получается из абсолютного пути рабочего каталога. Лезть сюда руками обычно не нужно — пользуйтесь hermes checkpoints status / prune / clear.
Соседний файл .checkpoints.lock координирует процессы и переживает очистку хранилища. Не удаляйте его, пока Hermes запущен.
Миграция с v1
До переписывания на v2 каждый рабочий каталог получал собственный полноценный теневой git-репозиторий прямо в ~/.hermes/checkpoints/<hash>/. Такая схема не умела дедуплицировать объекты между проектами, а её сборщик мусора был задокументированным no-op — хранилище росло без предела.
При первом запуске v2 все теневые репозитории эпохи pre-v2 переезжают в ~/.hermes/checkpoints/legacy-<timestamp>/, чтобы новая одно-хранилищная схема стартовала с чистого листа. Старая история /rollback всё ещё доступна — достаточно вручную заглянуть в архив через git. Когда убедитесь, что она вам больше не нужна, запустите:
hermes checkpoints clear-legacy
и освободите место. Архивы legacy к тому же подметаются авточисткой auto_prune по истечении retention_days.
Рекомендации
- Включайте контрольные точки только когда они реально нужны — через
hermes chat --checkpointsилиenabled: trueв профиле. - Перед восстановлением запускайте
/rollback diff— посмотрите, что изменится, и выберите правильную точку. - Используйте
/rollbackвместоgit reset, если хотите откатить только то, что наделал агент. - Иногда заглядывайте в
hermes checkpoints status, если работаете с контрольными точками постоянно: видно, какие проекты активны и во сколько обходится хранилище. - Сочетайте с git worktree для максимальной надёжности — держите каждую сессию Hermes в своём worktree/ветке, а контрольные точки добавьте сверху ещё одним слоем защиты.
Про запуск нескольких агентов параллельно на одном репозитории читайте в руководстве по git worktree.