Участие в разработке
Спасибо за вклад в Hermes Agent! Здесь разобраны настройка рабочего окружения, устройство кодовой базы и путь от написания кода до принятого PR.
Приоритеты вкладов
Вклады мы ценим в таком порядке:
- Исправления багов — сбои, некорректное поведение, потеря данных
- Кросс-платформенная совместимость — macOS, разные дистрибутивы Linux, WSL2
- Укрепление безопасности — инъекции через shell, инъекции в промпт, обход каталогов
- Производительность и надёжность — логика повторных попыток, обработка ошибок, graceful degradation
- Новые навыки — широко применимые (см. Создание навыков)
- Новые инструменты — нужны редко; большинство возможностей лучше реализовывать как навыки
- Документация — исправления, уточнения, новые примеры
Критерии оценки вкладов
Это слой намерений проекта, кратко изложенный в корневом AGENTS.md; здесь его развёрнутая
версия с примерами. Hermes выпускает много: большинство слияний — это исправления багов, а поверхность
продукта (платформы, провайдеры, модели, функции десктопа и TUI) расширяется намеренно. Сдержанность
относится к ядру агента и схеме инструментов модели, где за каждое добавление платят при каждом
вызове API: широко по краям, осторожно в узком месте.
Что нам нужно
- Чинить настоящие баги, и чинить хорошо. Воспроизведите симптом на актуальной
main, укажите точную строку, где он проявляется, и исправьте весь класс бага, включая соседние пути вызова. - Расширять охват по краям. Новые адаптеры, каналы, провайдеры, модели, функции десктопа, TUI и
дашборда приходят регулярно, в том числе крупные, если вписываются в существующий UX настройки и
конфигурации (
hermes tools,hermes setup, автоустановка), а не приклеивают сырую переменную окружения. - Разбирать файлы-монстры на чистые модули. Огромные механические PR с извлечением вида
+N/-N— желанная работа. Правило «каждая строка прослеживается до запроса» относится к функциональным PR; в заявленном рефакторинге сам запрос и есть извлечение. - Держать ядро узким. Предпочитайте по порядку: расширить существующий код → команда CLI + навык →
инструмент за сервисным условием (
check_fn) → плагин → MCP-сервер в каталоге → новый инструмент ядра (в последнюю очередь). См. «Лестницу следа» ниже. - Расширять, а не дублировать. Прежде чем добавлять модуль, менеджер или хук, проверьте, не покрывает ли сценарий существующая инфраструктура. Когда 3+ открытых PR интегрируют одну и ту же категорию (бэкенды памяти, провайдеры, нотификаторы), спроектируйте ABC и оркестратор, оберните существующую встроенную реализацию как первого провайдера, а конкурирующие PR превратите в плагины к нему.
- Контракты поведения вместо снимков. Тесты проверяют, как соотносятся два набора данных, и
никогда не замораживают текущее значение (см.
tests/AGENTS.md). - E2E-проверка, а не только зелёные юнит-моки. Всё, что затрагивает цепочки разрешения,
распространение конфигурации, границы безопасности, удалённые бэкенды или файловый/сетевой ввод-вывод,
должно пройти проверку реальным путём с настоящими импортами на временном
HERMES_HOME, а если изменение касается области профиля, то на двух (A→B→A). Моки прячут ошибки интеграции. - Безопасно для кэша, чередования и инвариантов. Сохраняйте кэширование промпта, строгое чередование ролей (никогда два сообщения одной роли подряд; никогда синтетическое пользовательское сообщение посреди цикла) и системный промпт, побайтово неизменный на протяжении всего разговора.
- Авторство контрибьютора сохраняется. Внешнюю работу забирайте через cherry-pick (rebase-merge), чтобы сохранилось авторство; надстраивайте сверху, а не переписывайте заново.
Чего нам не нужно (отклоняется, даже если сделано хорошо)
- Спекулятивная инфраструктура. Хуки, колбэки и точки расширения без конкретного потребителя. Добавить хук легко, а убрать после того, как на него завязались плагины, трудно. Хук с реальным, названным сценарием спекулятивным НЕ считается, даже если потребитель поставляется отдельно.
- Новые переменные окружения
HERMES_*для несекретной конфигурации..env— только для секретов. Поведенческие настройки (таймауты, пороги, флаги, параметры отображения) идут вconfig.yaml; если механизму нужна внутренняя переменная окружения, прокиньте её в коде. Отклоняйте документацию вида «задайте X в .env», если X не учётные данные. - Новый инструмент ядра там, где уже справляются терминал и файлы (или навык). Если единственная преграда — видимость файлов на удалённом бэкенде, чините монтирование, а не набор инструментов.
- Лазейки ленивого чтения у инструкционных инструментов. Никакой пагинации
offset/limitу инструментов, которые загружают содержимое, обязанное быть прочитанным целиком (навыки, промпты, плейбуки): модели читают первую страницу и пропускают остальное. - «Исправления», которые разрушают защищаемую функцию. Прежде чем ограничивать поведение,
прочитайте исходное намерение (
git log -p -S); найдите исправление, сохраняющее функцию. - Исходящая телеметрия и атрибуция использования без согласия. Никакой аналитики, меток
сторонних идентификаторов и атрибуции, пока нет общего пользовательского согласия (конфиг-условие,
запрос при настройке и переключатель в
hermes tools). Паркуйте такое за меткой. - Тесты-детекторы изменений, ломающий кэш код посреди разговора, мёртвый код, подключённый без E2E-доказательств, и плагины, которые лезут в файлы ядра. Плагины работают в рамках ABC и хуков, которые мы даём; если нужно больше, расширяйте общую поверхность плагинов, а не делайте исключение в ядре.
- Сторонние продукты, встроенные в дерево ядра. Бэкенды наблюдаемости, коннекторы к вендорскому
SaaS, аналитические дашборды и прочие плагины «чужого продукта» НЕ попадают в
plugins/: каждый превращается в нашу ношу при быстро меняющемся ядре ради бэкенда, которым мы не владеем. Выпускайте как отдельный репозиторий плагина (~/.hermes/plugins/или pip entry point) и продвигайте в Discord Nous Research, канал#plugins-skills-and-skins. Это решение о связанности, а не планка качества; такие PR закрываются с советом опубликовать плагин отдельно.
Прежде чем назвать это багом: проверьте предпосылку (и когда закрывать НЕ стоит)
Чаще всего хорошо написанный PR закрывают из-за неверной предпосылки или из-за того, что намеренный дизайн принят за пробел. Эти шаблоны подсказывают ревьюеру, что проверять, а чистильщику — когда PR закрывать небезопасно (в сомнительных случаях оставляйте открытым для человека):
- «Намеренный дизайн, а не пробел». Спросите, не в изоляции ли и состоит замысел. Профили —
независимые острова намеренно: PR с живым наследованием конфига от профиля по умолчанию закрыли,
потому что связывать профили — ровно то, что дизайн предотвращает (
--cloneуже закрывает «начать с моего профиля по умолчанию»). Прежде чем считать что-то недоделанным, прочитайтеgit log -p -S "<symbol>". - «Предпосылка не выдерживает проверки тем, как X работает на самом деле». Прежде чем принимать обоснование, проследите реальный рантайм. Настоящие закрытия: PR «повторная проба во время охлаждения» для лимита запросов (размыкатель срабатывает только на подтверждённо пустом бакете, поэтому повторная проба долбит заведомо пустой бакет); исправление учёта использования, чья новая ветка никогда не выполняется, потому что более ранняя проверка уже извлекла состояние. Если вы не можете указать точную строку, где проявляется баг, И показать, что исправление меняет поведение этой строки, предпосылка не проверена.
- «Отсутствие было сознательным». Восстановление «пропавших» файлов
__init__.pyсделало тестовое дерево импортируемым как пакет с точками, который затенил настоящий плагин и удалил егоregister()при импорте. Этот пропуск был несущим. - «Перегнули палку или воскресили подход, от которого мы ушли». Расползание объёма за пределы согласованной основы или возрождение направления, которое мейнтейнеры закрыли, отклоняется, даже если работает. Остальное предложите отдельным сфокусированным продолжением.
Сквозная мысль: прежде чем писать или сливать исправление, проверьте и утверждение, и намерение
по кодовой базе. Воспроизведение на актуальной main плюс построчное объяснение весомее правдоподобного
обоснования. Если намерение неясно, спросить дешевле, чем выпустить исправление, воюющее с дизайном.
Лестница следа (решение о новой возможности)
Выбирайте самую верхнюю (с наименьшим следом) ступень, которая корректно решает задачу:
- Расширить существующий код — вариация того, что уже есть. Ноль новой поверхности.
- Команда CLI + навык — конфигурация, состояние и инфраструктура, выразимые командами
shell; агент запускает
hermes <subcommand>по подсказкам навыка. Выбор по умолчанию для подписок, запланированных задач и настройки сервисов (hermes webhook,hermes cron,hermes tools). - Инструмент за сервисным условием (
check_fn) — нужны структурированные параметры и возвращаемые значения, И он появляется только при настроенном предварительном условии (инструменты Home Assistant, инструменты провайдеров памяти). Эта ступень задаёт достижимость и согласие на уровне всего процесса; возможность, меняющаяся от СЕССИИ к сессии (кто смотрит), — это именованный набор инструментов, который подмешивает резолвер наборов, а неcheck_fn; см.tools/AGENTS.md, раздел «Surface capability is a property of the SESSION». - Плагин — сторонний, нишевый или пользовательский; лежит в
~/.hermes/plugins/или в pip-пакете, обнаруживается в рантайме. - MCP-сервер (в каталоге) — это действительно инструмент, но не фундаментальный для ядра. Нулевой постоянный след в схеме ядра, пригоден для любого MCP-хоста, доступен через встроенный MCP-клиент.
- Новый инструмент ядра — только если он фундаментален, полезен почти каждому пользователю и недостижим через терминал и файлы или MCP-сервер (terminal, read_file, web_search, browser_navigate).
Распространённые сценарии участия
- Пишете собственный/локальный инструмент без правок ядра Hermes? Начните с Создание плагина Hermes
- Добавляете новый встроенный инструмент в само ядро Hermes? Начните с Добавление инструментов
- Создаёте новый навык? Начните с Создание навыков
- Добавляете нового провайдера инференса? Начните с Добавление провайдеров
Настройка окружения разработки
Требования
| Требование | Примечания |
|---|---|
| Git | С установленным расширением git-lfs |
| Python 3.14 | Для разработки сейчас используется закреплённый интерпретатор PM. Более широкие метаданные пакета >=3.11,<3.15 нужны, чтобы работали старые апдейтеры, а не чтобы текущая среда выполнения шла на старом Python. |
| Node.js | Версия из пина PM или любая, которую принимает поле engines в корневом package.json |
Окружение разработчика через PM
Подготовку, активацию, повседневные команды, изменение зависимостей и тестовые окружения описывает рабочий процесс разработчика PM. Выберите домашний каталог для разработки до настройки, чтобы экспериментальный код не мигрировал рабочие данные.
В каждой новой оболочке активируйте окружение из корня репозитория. Активация готовит checkout через PM и синхронизирует устаревшие зависимости.
Bash:
source ./activate
hermes --version
PowerShell:
. .\activate.ps1
hermes --version
Запускайте hermes для этого checkout. Активация объявляет его функцией для этого
worktree, поэтому она перекрывает глобальный псевдоним hermes и отказывается работать вне worktree.
Перед добавлением в оболочку активация PM синхронизирует инструменты и Python-зависимости. JS-workspace’ы
она не ставит и не переписывает лаунчеры и конфигурацию оболочки. deactivate возвращает прежнее окружение оболочки и убирает функцию.
Ручное окружение для разработки и тестов
Сначала подготовьте Python 3.14 по рабочему процессу разработчика PM.
Выполняйте команды ниже из этого checkout с подготовленным в нём Python. Используйте тот же
HERMES_HOME для разработки. PM должен суметь запуститься, прежде чем соберёт другое
окружение. В Windows перед сборкой зависимостей из исходников инициализируйте нативное
окружение сборки C++ для своей архитектуры.
Соберите независимый интерпретатор для тестов и инструментов редактора:
python -m pm.build_env --source . --out .venv --group dev --group test
PM собирает окружение из закоммиченного lock-файла и проверяет согласованность зависимостей,
прежде чем вернуть новый интерпретатор. Группа test включает зависимости для тестов
нативного лаунчера и в среду выполнения приложения не попадает. Если тестам нужна ещё
какая-то объявленная функция, добавьте её --extra.
Каталога назначения не должно существовать — даже пустого или в виде симлинка. Чтобы пересоздать окружение после изменения зависимостей, остановите его процессы и сначала осознанно удалите только это одноразовое окружение. Существующий каталог назначения PM не удаляет. Не меняйте окружение, собранное PM, голыми командами pip или uv.
Чтобы держать тестовое окружение вне checkout, замените .venv новым абсолютным
путём. Укажите в HERMES_PYTHON интерпретатор этого окружения:
- POSIX:
export HERMES_PYTHON="/absolute/path/to/hermes-dev/bin/python" - PowerShell:
$env:HERMES_PYTHON = 'C:\absolute\path\to\hermes-dev\Scripts\python.exe'
Канонический раннер находит .venv репозитория автоматически. Он очищает
PYTHONPATH, поэтому pytest должен быть установлен в собственное окружение интерпретатора.
Это тестовое окружение не заменяет выбор приложения и хранилище инструментов PM.
Не направляйте на него бандл-приложение и ничего не ставьте в пакет MSIX.
Для изолированного экземпляра разработки выберите одноразовый HERMES_HOME до запуска
команды из исходников. Настраивайте его через hermes setup, а не копируйте рабочие
учётные данные в checkout.
JavaScript-workspace’ы и сайт
Из корня репозитория выполните npm ci для десктопа, TUI, дашборда и общих
JS-workspace’ов. Сайт ставится отдельно:
npm ci --prefix website
npm run build:fast --prefix website
Берите версию Node/npm, которую принимает поле engines соответствующего package.json.
Нативным зависимостям десктопа может понадобиться ещё и платформенный тулчейн сборки.
Логотипы и иконки генерируются из assets/nous-girl-*.svg и
assets/backgrounds/. node scripts/generate-icons.mjs рендерит их Python-ом среды
выполнения Hermes (HERMES_PYTHON, иначе python из PATH): Pillow и resvg-py входят
в основные зависимости. Сгенерированные PNG/ICO/ICNS не коммитьте.
Запуск тестов
На любой машине используйте канонический раннер:
scripts/run_tests.sh
scripts/run_tests.sh tests/agent/ -v
В Windows запускайте скрипт через Bash. Если ни в локальном .venv, ни в venv нет
pytest, раннер принимает явно заданный HERMES_PYTHON из примера выше. Он очищает
учётные данные, изолирует HERMES_HOME и запускает каждый тестовый файл в отдельном
подпроцессе через scripts/run_tests_parallel.py. xdist не используется.
Когда tests/conftest.py перенаправляет рабочий HERMES_HOME во временный домашний
каталог сессии, он ставит внутренний маркер HERMES_TEST_SANDBOX_HOME. Благодаря этому
повторно импортированные тестовые фикстуры узнают собственную песочницу, а не принимают её
за ввод-вывод в настоящем домашнем каталоге. Сами этот маркер не задавайте: задайте
HERMES_HOME для одноразового каталога разработки, а изоляцию оставьте раннеру.
Для изменений в JS прогоняйте проверки соответствующих workspace’ов. E2E-тесты нативной установки и обновления идут на одноразовых CI-хостах и никогда — на живом приложении разработчика. Команды PM и то, кто владеет средой выполнения, описаны в разделе Управление пакетами.
Стиль кода
- PEP 8 с разумными исключениями (строгое ограничение длины строки не применяется)
- Комментарии: только там, где нужно пояснить неочевидный замысел, компромисс или особенность API
- Обработка ошибок: перехватывайте конкретные исключения; для неожиданных ошибок используйте
logger.warning()/logger.error()сexc_info=True - Кросс-платформенность: никогда не предполагайте Unix (подробнее ниже)
- Пути, безопасные для профиля: никогда не хардкодьте
~/.hermes— для путей в коде беритеget_hermes_home()изhermes_constants, а для пользовательских сообщенийdisplay_hermes_home(). Полные правила — в AGENTS.md.
Кросс-платформенная совместимость
См. Поддержка платформ. На нативном Windows shell-команды выполняются через Git Bash (из Git for Windows). Дашборд использует POSIX PTY в Unix и мост pywinpty/ConPTY в Windows. Доступность зависит от того, поддерживает ли конкретная машина нужные нативные зависимости. Если вы много разрабатываете под Windows, перед пушем прогоните линтер для типичных Windows-ошибок (scripts/check-windows-footguns.py).
При написании кода учитывайте следующие правила:
- Не добавляйте незащищённые ссылки на
signal.SIGKILL. На Windows этот сигнал не определён. Либо используйтеgateway.status.terminate_pid(pid, force=True)(централизованный примитив, который вызываетtaskkill /T /Fна Windows и SIGKILL на POSIX), либо делайте фолбэк черезgetattr(signal, "SIGKILL", signal.SIGTERM). - Проверяйте, жив ли процесс, через
psutil.pid_exists(). Не используйтеos.kill(pid, 0)на Windows: безопасной проверкой он не является. - Не навязывайте терминалу POSIX-семантику.
os.setsid,os.killpg,os.getpgid,os.forkна Windows бросают исключения — оборачивайте их вif sys.platform != "win32":илиif os.name != "nt":. - Указывайте кодировку текста явно. Пользовательские UTF-8-файлы читайте с
utf-8-sig, чтобы принимать BOM в начале. Записывайте вutf-8без добавления BOM. - Используйте
pathlib.Path/os.path.join— никогда не склеивайте пути вручную через/. Для строк, которые нам возвращает сама ОС, это не так критично, а вот для строк, которые мы сами формируем для передачи в подпроцессы, — важно.
Ключевые шаблоны:
1. Кодировка файлов
В некоторых окружениях .env-файлы сохраняются в кодировках, отличных от UTF-8:
try:
load_dotenv(env_path)
except UnicodeDecodeError:
load_dotenv(env_path, encoding="latin-1")
2. Управление процессами
os.setsid(), os.killpg() и обработка сигналов на разных платформах ведут себя по-разному:
import platform
if platform.system() != "Windows":
kwargs["preexec_fn"] = os.setsid
3. Разделители путей
Используйте pathlib.Path вместо конкатенации строк через /.
Вопросы безопасности
У Hermes есть доступ к терминалу. Безопасность важна.
Существующие защитные механизмы
| Слой | Реализация |
|---|---|
| Передача пароля sudo | Используется shlex.quote() для защиты от shell-инъекций |
| Обнаружение опасных команд | Regex-паттерны в tools/approval.py с запросом подтверждения у пользователя |
| Инъекция промпта через cron | Сканер блокирует паттерны переопределения инструкций |
| Список запрещённых путей на запись | Защищённые пути разрешаются через os.path.realpath(), чтобы исключить обход через симлинки |
| Защита навыков | Сканер безопасности для навыков, установленных из хаба |
| Песочница выполнения кода | Дочерний процесс запускается без ключей API |
| Усиление контейнера | Docker: все capabilities сброшены, нет эскалации привилегий, ограничения PID |
Код, затрагивающий безопасность
- Всегда используйте
shlex.quote()при подстановке пользовательского ввода в shell-команды - Разрешайте симлинки через
os.path.realpath()перед проверками контроля доступа - Не логируйте секреты
- Оборачивайте выполнение инструментов в широкие блоки перехвата исключений
- Тестируйте на всех платформах, если изменение касается файловых путей или процессов
Процесс pull request
Именование веток
fix/description # Исправления багов
feat/description # Новые функции
docs/description # Документация
test/description # Тесты
refactor/description # Рефакторинг кода
Перед отправкой
- Запустите тесты:
scripts/run_tests.shдля паритета с CI. Прямой вызовpython -m pytest ...используйте, только если обёртка недоступна или вы намеренно отлаживаетесь в обход неё. - Протестируйте вручную: запустите
hermesи проверьте затронутый код - Оцените кросс-платформенное влияние: учтите macOS, Linux, WSL2 и нативный Windows. Если изменение касается файлового ввода-вывода, управления процессами, работы с терминалом, подпроцессов или сигналов, прогоните
scripts/check-windows-footguns.py. - Держите PR сфокусированным: одно логическое изменение на PR
- Изменения промпта или схемы инструментов: если падает
tests/ci/test_prompt_surface.py, ваше изменение меняет системный промпт или схему инструмента, которую отправляет каждая сессия. Перегенерируйте снимок командойscripts/run-in-hermes-env python scripts/ci/prompt_surface.py renderи закоммитьте его. Изменения здесь требуют одобрения мейнтейнера (см. Сборка промпта). - Новые ключи конфигурации или slash-команды: если падает
tests/ci/test_docs_drift.py, у добавленного вами ключаDEFAULT_CONFIGили slash-команды нет документации. Упомяните ключ через точки (agent.max_turns) или покажите его в примере YAML подwebsite/docs/(обычноuser-guide/configuration.md), а slash-команды добавьте вreference/slash-commands.md. Если ключ действительно должен остаться недокументированным,python scripts/ci/check_docs_drift.py --updateвнесёт его вscripts/ci/docs_drift_baseline.json, где его увидят ревьюеры. Ключи, которые были недокументированы на момент появления проверки, уже в базовой линии; документирование такого ключа никогда не вызывает падения.
Описание PR
Включите:
- Что изменено и зачем
- Как протестировать изменение
- На каких платформах вы проверяли
- Ссылки на связанные issues
Сообщения коммитов
Мы используем Conventional Commits:
<type>(<scope>): <description>
| Тип | Использование |
|---|---|
fix | Исправления багов |
feat | Новые функции |
docs | Документация |
test | Тесты |
refactor | Рефакторинг кода |
chore | Сборка, CI, обновление зависимостей |
Скоупы: cli, gateway, tools, skills, agent, install, whatsapp, security
Примеры:
fix(cli): prevent crash in save_config_value when model is a string
feat(gateway): add WhatsApp multi-user session isolation
fix(security): prevent shell injection in sudo password piping
Локальные чек-листы ревью в репозитории: .agents/checks/*.md
Проекты, построенные на Hermes (или ревьюируемые им), могут хранить чек-листы для ревьюеров прямо в репозитории, в .agents/checks/. Каждый файл — сфокусированный чек-лист в чистом markdown, который агент загружает перед ревью изменения, затрагивающего соответствующую область:
.agents/
checks/
security.md # например: «проверить diff на shell-интерполяцию; убедиться, что вызовы subprocess экранируют аргументы»
migrations.md # например: «каждое изменение схемы сопровождается бэкфиллом и заметкой об откате»
public-api.md # например: «изменились экспортируемые сигнатуры? пометить для ревью на предмет semver»
Соглашения, которые делают такие чек-листы полезными:
- Одна тема на файл, названный по теме. Небольшие файлы читаются целиком, а монолитный
checklist.md— только пробегается по диагонали. - Формулируйте пункты как проверяемые действия («запустить X и убедиться в Y»), а не как пожелания («код должен быть безопасным»).
- Укажите в начале файла область применения — какие пути или типы изменений он покрывает, — чтобы агент (или человек) мог дёшево пропустить неподходящие чек-листы.
- Держите их в системе контроля версий рядом с кодом, который они защищают: они меняются вместе с кодовой базой, и PR, меняющий правила, меняет чек-лист в том же диффе.
Когда вы просите Hermes проверить PR в репозитории с .agents/checks/, скажите ему (или научите через навык) сначала прочитать соответствующие чек-листы и отчитаться по ним. Это даёт агентам ревью специфичную для проекта планку, которую упускают типовые промпты для ревью.
Сообщения об ошибках
- Используйте GitHub Issues
- Указывайте: ОС, версию Python, версию Hermes (
hermes --version), полный стектрейс ошибки - Добавляйте шаги для воспроизведения
- Проверяйте существующие issues, чтобы не создавать дубликаты
- Уязвимости безопасности сообщайте в приватном порядке
Сообщество
- Discord: discord.gg/NousResearch
- GitHub Discussions: для обсуждения архитектурных решений и предложений по дизайну
- Skills Hub: публикуйте специализированные навыки и делитесь ими с сообществом
Лицензия
Отправляя свои изменения, вы соглашаетесь с тем, что они будут распространяться под лицензией MIT.