Контрольные точки и /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 делает следующее:

  1. Проверяет, что нужный коммит действительно есть в теневом хранилище.
  2. Снимает слепок состояния перед откатом — чтобы позже можно было «отменить отмену».
  3. Восстанавливает отслеживаемые файлы в рабочем каталоге — сохраняя ваши ручные правки (см. ниже).
  4. Отменяет последний ход диалога, чтобы контекст агента совпадал с восстановленным состоянием файлов.

Ручные правки сохраняются по умолчанию

/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.

перевод и редактура:

обновлено

⌕ ESC