24 KiB
Hermes — апстрим vs наш форк
Дата: 2026-09-15
Статус: актуально — анализ расхождения сделан, стратегия подтяжки НЕ выбрана
Репо: ~/.hermes/hermes-agent (сабмодуль, форк mallexxx/hermes-agent)
Текущее решение: не принято
На 2026-09-15 Alex'у представлены 3 варианта (A merge / B перенос на свежий апстрим / C заморозка). Выбор не сделан. Работа над подтяжкой не начата. Наш форк запушен и стабилен — можно спокойно решать.
Открытый вопрос, определяющий выбор: нужны ли нам вообще 24k коммитов
апстрима? Мы используем Hermes как Zulip-бота с webhook-флоу; апстрим растёт
десктопом/TUI/платформами, которые нам не нужны. Точечный cherry-pick
(security-фиксы, фиксы agent/, новые модели) может быть достаточен.
Масштаб расхождения
| Метрика | Значение |
|---|---|
| Коммитов апстрима впереди | 24 407 |
| Наших коммитов | 32 |
| Common ancestor | 4ed63170e4 (2026-06-04) |
| Дата последнего коммита апстрима | 2026-09-15 |
| Наша версия | 0.15.1 |
| Версия апстрима | 0.21.3 |
| Файлов изменено апстримом | 12 634 |
| Строк: +2 362 152 / −763 849 |
Вывод: это не «догнать на пару недель», а ~3.5 месяца и 24k коммитов. Апстрим ушёл на мажорные версии вперёд (0.15 → 0.21).
Ремоуты
| Remote | URL | Роль |
|---|---|---|
origin |
git@github.com:mallexxx/hermes-agent.git |
наш форк |
upstream |
https://github.com/NousResearch/hermes-agent.git |
источник |
Главные изменения апстрима
1. Платформы переехали из ядра в плагины ⚠️ КРИТИЧНО
Апстрим удалил telegram.py, slack.py, matrix.py, discord.py из
gateway/platforms/ — теперь они живут в plugins/platforms/.
Состав plugins/platforms/ у апстрима (19):
a2a, buzz, dingtalk, discord, email, feishu, google_chat, homeassistant, irc, line, matrix, mattermost, ntfy, photon, raft, simplex, slack, sms, teams, telegram, wecom, whatsapp
Наш plugins/platforms/ (8): discord, google_chat, irc, line, mattermost, ntfy, simplex, teams
⚠️ Zulip НЕТ ни в ядре апстрима, ни в plugins/platforms/ апстрима.
Проверено: git ls-tree -r upstream/main | grep -i zulip → пусто.
Zulip — полностью наша разработка:
gateway/platforms/zulip.py— не существует в апстримеtools/zulip_thread_tool.py— не существует в апстриме
Это значит: наш Zulip-адаптер живёт в устаревшем месте (gateway/platforms/),
которое апстрим планомерно опустошает. При подтяжке его придётся переносить в
plugins/platforms/ — иначе он повиснет на api, которого больше нет.
2. Смежные подсистемы
Топ-скоупы по числу feat/refactor коммитов:
| Скоуп | Коммитов | Что это |
|---|---|---|
desktop |
773 | Десктоп-приложение — крупнейшая новая подсистема |
hermes_cli |
607 | CLI переработан |
gateway |
412 | Ядро гейтвея |
tools |
339 | Инструменты |
agent |
217 | Ядро агента |
cli / tui / tui_gateway |
141/100/76 | TUI (Ink/React) |
state |
106 | Персистентность сессий |
cron |
91 | Планировщик |
computer_use |
86 | Управление десктопом |
mcp |
79 | MCP-клиент |
relay |
54 | Новая подсистема релея |
Типы коммитов: fix 10 994, refactor 4 158, feat 2 113, test 1 836.
Читается так: апстрим прошёл большую волну рефакторинга (4158) + массовый
десктоп/TUI-фронт. Наши правки в gateway/run.py (13 коммитов) и
gateway/stream_consumer.py (9) — ровно в эпицентре этого рефакторинга.
Масштаб по коду (не «перетаскивание папок»)
Апстрим — это не реорганизация, а +322 новых модуля. Считать так:
| Область | У нас | У апстрима | Нового |
|---|---|---|---|
agent/ |
88 файлов | 229 | +144 |
tools/ |
86 файлов | 262 | +178 |
hermes_state* |
1 монолит | 25 модулей | разбит |
apps/ |
— | 2674 файла | desktop-приложение |
evals/ |
— | 203 файла | eval-фреймворк |
contributors/ |
— | 1176 | данные контрибьюторов |
plugin-catalog/ |
— | 27 | каталог плагинов |
Команда для повторной проверки:
cd ~/.hermes/hermes-agent
comm -13 <(git ls-tree --name-only main:agent/ | sort) \
<(git ls-tree --name-only upstream/main:agent/ | sort) | wc -l
Что реально нового в апстриме (по функциональности)
1. Desktop-приложение (apps/desktop, 2674 файла) — крупнейшая подсистема
(773 коммита в скоупе desktop). Нативный GUI. Внутри — bootstrap-installer,
shared. Это фронт на TypeScript/Ink.
2. Bot Mode (website/docs/user-guide/bot-mode.md) — прямо про наш кейс.
Профили превращаются в roster именованных ботов: у каждого своя роль, модель,
память, скиллы, аватар. Боты ходят в групповые чаты и пишут друг другу.
Ключевое: «A Bot is a Hermes profile» — никакого нового примитива, всё видно
из CLI (hermes -p <bot> chat), роутины в hermes cron list.
Есть «forever-chat» на бота: /new внутри перехватывается в /compact.
Секции (папки) для группировки, скрытие ботов, фильтр «Active now».
Сопоставление с нашим: у нас Eagle + Whale + Balda роутятся через самодельный
plugins/whale-thread-guard (файл thread_ownership.json). У апстрима это
нативная фича с UI. ⚠️ Открытый вопрос для Alex — не выбрасывается ли наш
роутинг в пользу нативного (см. «Ключевой вопрос» ниже).
3. Автономные циклы — три механизма:
/goal— стоящая цель через turns; lightweight judge-модель после каждого turn проверяет достижение, агент сам кормит continuation prompt до успеха. Их версия Ralph loop (вдохновлено Codex CLI 0.128.0)./loop— перезапуск промпта по таймеру внутри сессии; каждый wakeup — это реальный turn. Их версия Claude Code/loop(алиас/proactive)./heartbeat— один промпт на сессию, срабатывает когда сессия idle. Разница:/goaljudge-driven («работай пока не достигнешь»),/looptimer-driven («делай это каждые N минут»).
4. Encrypted credential vault (agent/vault_store.py) — зашифрованное
(Fernet) локальное хранилище для browser autofill. Profile-scoped, model-blind:
модель видит только opaque handles + метаданные (kind, label, origin), значения
резолвятся server-side и не попадают в tool results / логи / session DB.
Три вида: login, payment, address. Ключ и файл 0600 в <HERMES_HOME>/vault/.
Портировано из Merit-Systems/OpenInstinct (MIT).
5. Turn-цикл разобран на 29 модулей — turn_facade, turn_preflight,
turn_preflight_gate, turn_recovery, turn_overflow, turn_truncation,
turn_tool_round, turn_finalizer, turn_liveness, turn_retry_state и др.
run_conversation из монолита стал конвейером.
6. MCP переписан — 20 модулей: mcp_tool_discovery, mcp_oauth_provider,
mcp_death_supervisor, mcp_tool_sampling, mcp_tool_schema, mcp_schema_cache,
mcp_tool_health, mcp_tool_scope, mcp_oauth_device и др.
7. Браузер — 16 модулей browser_tool_*: CDP, cloud, lightpanda fallback,
snapshot, vision, origin, session, lifecycle, install, real_profile,
supervisor_dialogs/frames, eval_policy, vault_tool.
8. native/fts5_cjk — C-расширение SQLite FTS5 для китайского/японского/
корейского токенизации полнотекстового поиска. Нативная сборка
(build.sh + vendor/sqlite3.h). Прямо относится к багу китайского вывода —
см. deepseek-language-drift.
9. Skills Hub — 8 модулей: skills_hub_github, skills_hub_official,
skills_hub_clawhub, skills_hub_skillssh, skills_hub_search, skills_hub_install,
skills_hub_sources, skills_hub_models. Плюс skill_linter, skill_ledger,
skill_manager_batch, skill_manager_guards.
10. Прочее новое: pets (питомцы в UI), skins, personality,
mixture-of-agents (moa_loop.py, moa_trace.py), honcho (memory-провайдер),
code_kernel + code_kernel_remote (Python-кирнел), deliverable-mode (файлы
как нативные вложения в мессенджерах), credential-pools, provider-routing,
subscription-proxy, tool-gateway, tool-search, kanban-multi-gateway,
relay (NeMo Relay runtimes, 54 коммита), lsp, heartbeat, goals, loops,
curator, skills, context-references, document-extraction, batch-processing,
computer-use, codex-app-server-runtime, plugin-catalog, extending-the-dashboard.
Новые top-level каталоги: apps/, native/, evals/, contributors/,
plugin-catalog/, providers/, locales/, web/, website/i18n.
Ответ: есть ли замена Zulip?
Нет. Проверено по существу — по модели общения, а не по имени платформы.
| Платформа | Треды | Self-hosted | Stream+topic |
|---|---|---|---|
matrix |
✅ threads | ✅ | ❌ room-based |
mattermost |
✅ thread-mode replies | ✅ | ❌ channel-based |
slack |
✅ threads | ❌ SaaS | ❌ |
teams |
✅ | ❌ SaaS | ❌ |
google_chat |
❌ | ❌ SaaS | ❌ |
irc |
❌ | ✅ | ❌ flat |
simplex |
❌ groups | ✅ p2p | ❌ |
a2a |
— | ✅ | — (agent-to-agent, не чат) |
Проверка на stream+topic: git grep -li 'stream.*topic' upstream/main -- plugins/platforms/
→ только dingtalk и telegram, и там это не топик-модель.
Ключевое различие: Zulip — единственная известная чат-платформа с нативной
моделью «поток → топик», где топик = адресуемая сущность с собственным именем.
Matrix и Mattermost дают треды как свойство сообщения (reply-thread), а не как
адресуемую сущность. У нас stream::topic — это ключ роутинга
(thread_ownership.json: "stream::topic": "eagle"|"whale"), на их модели такой
ключ не построить.
Вывод: миграция на matrix/mattermost = потеря топик-роутинга между ботами.
whale-thread-guard на их модели не переносится.
Подробности платформ: plugins/platforms/<name>/plugin.yaml — там description,
requires_env, optional_env с описаниями (MATTERMOST_REPLY_MODE: 'thread'|'off',
MATRIX_HOMESERVER, mention-gating и т.д.).
Ответ: сделаны ли custom prompts как у нас?
Нет, аналога не существует. Проверено:
git grep -l 'prompt_overrides' upstream/main # пусто
git grep -l '_load_prompt_block' upstream/main # пусто
В апстриме все блоки промпта — захардкоженные константы в
agent/prompt_builder.py: MEMORY_GUIDANCE, USER_PROFILE_GUIDANCE,
SESSION_SEARCH_GUIDANCE, SKILLS_GUIDANCE, KANBAN_GUIDANCE,
TOOL_USE_ENFORCEMENT_GUIDANCE, TASK_COMPLETION_GUIDANCE,
PARALLEL_TOOL_CALL_GUIDANCE, OPENAI_MODEL_EXECUTION_GUIDANCE,
GOOGLE_MODEL_OPERATIONAL_GUIDANCE, HERMES_AGENT_HELP_GUIDANCE (+ вариант
_NO_SKILLS), DEFAULT_AGENT_IDENTITY, PLATFORM_HINTS (по платформе).
build_system_prompt_parts() в апстриме просто делает stable_parts.append(CONST)
— точки подмены из файла нет.
Что у апстрима ЕСТЬ для кастомизации:
SOUL.md— идентичность агента (есть и у нас, грузится черезload_soul_md())- context-файлы из CWD (
AGENTS.md,HERMES.md) —_find_hermes_md(),_scan_context_content(),_strip_yaml_frontmatter() system_messageиз конфига
Чего НЕТ: подмены отдельных блоков промпта файлами. То есть наш
_load_prompt_block() + agent.prompt_overrides — уникальная фича форка
(коммит 207a1de16). См. hermes-agent-improvements.
Практический смысл различия: у нас правка промпта = редактирование .md
в hermes-whale/review/ без касания Python. У них = правка Python-константы в
prompt_builder.py, который апстрим постоянно переписывает (131 коммит).
agent/thread_scoped_output.py — существует в апстриме. У нас thread-scoped
память сделана с нуля (972004546f). Стоит сравнить перед вариантом B: возможно,
у апстрима есть готовое решение лучше. Не проверено детально.
НАШИ 32 КОММИТА
Сгруппированы по темам:
Zulip-платформа (наша, в апстриме отсутствует):
b0f097a299feat: add Zulip platform adapter (parallel to Discord)ce093b0a75zulip: switch approval prompt emojis ✅/❌ → 👍/👎b1e36eb2d7fix(zulip): lock→locked emoji namef39671ba97fix(routing): pre_gateway_dispatch before session guardd4e98a8b07/ef69e76413/40149225af/ddc6aa7c46webhook: zulip in BUILTIN_DELIVER_PLATFORMS, session_chat_id из X-Chat-Id8255ea0c64feat(zulip): session_search читает Zulip thread history049922d8ccfix(session_search): dual Zulip+SQLite recall, FTS5 sanitization
Webhook no-edit streaming (наш флоу без редактирования сообщений):
fa271d2d19,ee41e8a847,9003d4cbbe(revert),1ac06d593a,9253fe52cc,88cbb77b95,6c95153aa8,eb8dd7b5ed,a92840dcde,a08527e85c,a88bec1762,5d3e95841b,ec9efce7e7
Фичи профиля Whale:
972004546ffeat: thread-scoped memory + configurable memory instruction4ebad4f692test: thread-scoped memory persistence, drift guard, snapshotda146da5fafeat: configurable background review prompts and allowed toolsets207a1de16cfeat: load stable system-prompt blocks from config prompt_overrides
Прочее:
6e09a3704afix(approval): interrupt pending approvals on user messagea17d02f31efix(api_server): tool.progress → plain-text chunks81f10c306fapproval: reaction emoji на webhook
Риск конфликтов при merge
Dry-run git merge-tree → 16 конфликтных файлов, 160 конфликтных маркеров.
| Файл | Наших коммитов | Коммитов апстрима | Риск |
|---|---|---|---|
gateway/run.py |
13 | 974 | 🔴 экстремальный |
cron/scheduler.py |
1 | 307 | 🟠 |
agent/agent_init.py |
3 | 226 | 🟠 |
gateway/platforms/base.py |
2 | 208 | 🟠 |
gateway/platforms/api_server.py |
1 | 177 | 🟠 |
agent/model_metadata.py |
1 | 164 | 🟡 |
tools/approval.py |
1 | 147 | 🟡 |
agent/prompt_builder.py |
0 | 131 | auto-merge ok |
gateway/stream_consumer.py |
9 | 73 | 🟠 (наш файл, апстрим переписал) |
agent/system_prompt.py |
2 | 65 | 🟡 |
gateway/platforms/webhook.py |
7 | 59 | 🟠 |
toolsets.py |
2 | 62 | 🟡 |
tools/session_search_tool.py |
2 | 41 | 🟢 |
tools/memory_tool.py |
1 | 34 | 🟢 |
| остальные | 1 | <30 | 🟢 |
Худший случай: gateway/run.py — 974 коммита апстрима против наших 13.
Это фактически переписывание файла. Конфликты там будут не «принять/отклонить
строку», а разбор чужой новой архитектуры.
Стратегии подтяжки
Вариант A: merge (сейчас) — НЕ рекомендуется
git merge upstream/main → 16 конфликтных файлов, ручной разбор 160 маркеров
в горячих файлах. Оценка: дни на run.py + stream_consumer.py, риск
потерять наши webhook/Zulip-правки.
Вариант B: перенос нашей функциональности на свежий апстрим (рекомендуется)
Взять чистый upstream/main как новую базу, затем перенести наши фичи
патчами заново:
- Сохранить наши diff'ы как patch-файлы (
git format-patch) - Форк от свежего апстрима
- Переносить по темам: Zulip-адаптер (в
plugins/platforms/!), 3 фичи Whale (thread-scoped memory, bg-review, prompt_overrides), webhook-стриминг - Webhook-стриминг — вероятно уже решён апстримом, проверить перед переносом
Плюс: результат на актуальной кодовой базе, без 24k коммитов долга. Минус: ручная работа по переносу, ~1-2 дня.
Вариант C: заморозить и жить на форке
Принять, что мы отдельный продукт. Апстрим берём точечно (git cherry-pick
отдельных фиксов безопасности). Дёшево сейчас, растёт долг потом.
Ключевой вопрос перед подтяжкой
Нужны ли нам вообще 24k коммитов апстрима? Мы используем Hermes как Zulip-бота с webhook-флоу. Ядро апстрима прирастает десктопом, TUI и платформами (telegram/slack/...), которые нам не нужны. Реальная ценность от апстрима для нас:
- фиксы безопасности (
tools/tirith_security.py, approval) - фиксы багов в
agent/(метаданные моделей, сжатие контекста) - поддержка новых моделей/провайдеров
Это можно брать точечно (cherry-pick), не таща весь долг.
Что уточнилось 2026-09-15 (второй проход)
Alex задал три прямых вопроса, ответы получены (детали — в разделах выше):
| Вопрос | Ответ |
|---|---|
| Что нового в апстриме? | Не только платформы: +322 модуля, desktop, Bot Mode, /goal /loop /heartbeat, vault, turn-рефакторинг, MCP, Skills Hub |
| Есть ли замена Zulip? | Нет — ни одна платформа не даёт stream+topic как адресуемую сущность |
| Сделаны ли custom prompts как у нас? | Нет — блоки захардкожены, prompt_overrides уникален для форка |
Новый открытый вопрос (важнее прежних): Bot Mode апстрима делает нативно
то, что у нас держится на самодельном whale-thread-guard + thread_ownership.json.
Если переезжать на вариант B — надо решить, переносить наш роутинг или
выбрасывать в пользу нативного Bot Mode. Этот вопрос не задан Alex'у явно,
вынесен как следующий шаг.
Также не проверено: agent/thread_scoped_output.py в апстриме vs наша
thread-scoped память — возможно, у них готовое решение лучше нашей реализации.
Статус: стратегия подтяжки по-прежнему не выбрана. Форк запушен и стабилен.
Команды для проверки расхождения
cd ~/.hermes/hermes-agent
# Обновить инфу об апстриме
git fetch upstream
# Насколько отстали
git rev-list --count main..upstream/main # апстрим впереди
git rev-list --count upstream/main..main # мы впереди
# Общий предок
git merge-base main upstream/main
# Изменения по нашим файлам
git diff --name-only $(git merge-base main upstream/main)..main | sort -u
# Насколько апстрим трогал конкретный файл
git rev-list --count $(git merge-base main upstream/main)..upstream/main -- gateway/run.py
# Dry-run merge (безопасно, не трогает рабочую копию)
git merge-tree --write-tree --name-only main upstream/main
# Есть ли файл в апстриме
git cat-file -e upstream/main:gateway/platforms/zulip.py && echo yes || echo NO
Связанное
- hermes-git-repo — структура репо, .gitignore, что коммитить
- hermes-whale-system-prompt — prompt_overrides (наша фича)
- hermes-memory-architecture — thread-scoped память (наша фича)