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

214 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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) — ровно в эпицентре этого рефакторинга.
## Наши 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-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` как новую базу, затем **перенести наши фичи
патчами заново**:
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), не таща весь долг.
## Команды для проверки расхождения
```bash
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 память (наша фича)