Files
obsidian-vault/personal/tech/hermes-fork-vs-upstream.md
T

24 KiB
Raw Blame History

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. Разница: /goal judge-driven («работай пока не достигнешь»), /loop timer-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-платформа (наша, в апстриме отсутствует):

  • b0f097a299 feat: add Zulip platform adapter (parallel to Discord)
  • ce093b0a75 zulip: switch approval prompt emojis /👍/👎
  • b1e36eb2d7 fix(zulip): lock→locked emoji name
  • f39671ba97 fix(routing): pre_gateway_dispatch before session guard
  • d4e98a8b07 / ef69e76413 / 40149225af / ddc6aa7c46 webhook: zulip in BUILTIN_DELIVER_PLATFORMS, session_chat_id из X-Chat-Id
  • 8255ea0c64 feat(zulip): session_search читает Zulip thread history
  • 049922d8cc fix(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:

  • 972004546f feat: thread-scoped memory + configurable memory instruction
  • 4ebad4f692 test: thread-scoped memory persistence, drift guard, snapshot
  • da146da5fa feat: configurable background review prompts and allowed toolsets
  • 207a1de16c feat: load stable system-prompt blocks from config prompt_overrides

Прочее:

  • 6e09a3704a fix(approval): interrupt pending approvals on user message
  • a17d02f31e fix(api_server): tool.progress → plain-text chunks
  • 81f10c306f approval: reaction emoji на webhook

Риск конфликтов при merge

Dry-run git merge-tree16 конфликтных файлов, 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 как новую базу, затем перенести наши фичи патчами заново:

  1. Сохранить наши diff'ы как patch-файлы (git format-patch)
  2. Форк от свежего апстрима
  3. Переносить по темам: Zulip-адаптер (в plugins/platforms/!), 3 фичи Whale (thread-scoped memory, bg-review, prompt_overrides), webhook-стриминг
  4. 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

Связанное