diff --git a/agents/inbox/2026-08-23-A9-antigravity-quotas-models-migration.md b/agents/inbox/2026-08-23-A9-antigravity-quotas-models-migration.md index da36956..776f233 100644 --- a/agents/inbox/2026-08-23-A9-antigravity-quotas-models-migration.md +++ b/agents/inbox/2026-08-23-A9-antigravity-quotas-models-migration.md @@ -109,6 +109,83 @@ HermesHubSetup.cs(968,18): warning CS0219: Переменной "isRepair" пр --- +## P0-00. Hub перехватывает КАЖДЫЙ вызов Hermes как `orchestrator` + +Это самое важное в задании. Владелец сообщил: «зашёл в Гермеса, а там наш хаб не работает, основной оркестратор не выбрался», и сделал вывод, что Hub к Hermes не привязан. Вывод неверный, а положение хуже: **Hub привязан и активно ломал Hermes.** + +Что установлено разбором кода Hermes и его журналов: + +1. Плагин регистрируется штатно. `plugins.py:4789` берёт `register` у модуля, корневой `__init__.py` его экспортирует, `ctx.register_middleware("llm_execution", …)` — допустимое имя (`hermes_cli/middleware.py:23`). Перехватчик **срабатывает на каждом обращении к модели**, это видно в трейсбеке `agent.log`. + +2. **Hermes не передаёт роль.** `agent/conversation_loop.py:2950` передаёт `task_id`, `turn_id`, `api_request_id`, `session_id`, `platform`, `model`, `provider`, `base_url`, `api_mode`, `api_call_count` — и всё. Ключа `role` в вызове нет. + +3. Поэтому `resolve_role` доходит до последней строки и возвращает `config.default_role`. **Каждый вызов Hermes маршрутизируется как `orchestrator`.** + +4. Цепочка `orchestrator` у владельца исчерпана целиком: + +``` +ag-orch-fallback skipped_unhealthy +codex-orch 429: Your account is not active, please check your billing details +opengo-3 No API key found for OpenCode Go profile 'opengo-3' +ag-w1, ag-w3 Antigravity error: agy error: authentication failed or timed out +``` + +5. Роутер возвращал «⚠️ Hermes Router Failover Exhausted» **как ответ ассистента**, и Hermes показывал это вместо ответа модели — хотя его собственный провайдер работал. + +**Немедленная часть уже исправлена ревьюером** (`2d62d39`) и развёрнута владельцу: при `router_error` вызов уходит дальше по цепочке через `next_call`, отказ пишется в журнал уровнем `warning` с полным следом. Регрессия закрыта тестом `tests/test_plugin_passthrough.py`. Правку не откатывать. + +Принцип, который она закрепляет и который должен соблюдаться дальше: **плагин может улучшить маршрутизацию, но не имеет права сделать Hermes хуже, чем без него.** Любой отказ роутера — это молчаливый пропуск вниз плюс запись в журнал, а не подмена ответа. + +**Что требуется от вас — устранить причину, а не последствие.** + +Сейчас Hub перехватывает все вызовы Hermes и на каждом сначала пробует цепочку `orchestrator`: это лишняя задержка и расход квоты не той роли, даже когда пропуск отработал правильно. + +1. **Определить роль честно.** Разобраться, что из переданного Hermes пригодно как признак роли: `task_id`, `session_id`, `platform`, `model`, `provider`. Если надёжного признака нет — **не угадывать**. Эвристика в `resolve_role`, которая ищет в системном сообщении подстроки «developer», «coding agent», «review agent», — это гадание по тексту промпта, и оно тоже подлежит пересмотру. + +2. **Не претендовать на вызов без роли.** Если роль не определена достоверно, Hub не должен подменять выбор Hermes: пропускать вниз сразу, не тратя попыток. Роль по умолчанию для внешнего перехвата — неверная модель поведения. + +3. **Описать честную границу продукта.** Hermes ведёт собственные профили (`agy-01`…`agy-06`, `worker-fast`, `worker-research`, `worker-review`, `worker-code`, `worker-code-2`, `deepseek`) в каталоге `profiles` своего домашнего каталога, и его `delegate_task` настроен отдельно (`max_concurrent_children=3`, `provider=opencode-go`, `model=kimi-k2.7-code`). Профили Hub (`ag-w1`, `codex-orch`, `opengo-*`) — **другое множество идентификаторов**, ничем с ними не связанное. То есть один и тот же аккаунт Google живёт в двух учётных системах под разными именами. + + Написать в `docs/UI_STATE_CONTRACT.md` раздел о границе: что Hub видит от Hermes, чего не видит, чем управляет и чем не управляет. Без этого любые обещания интерфейса о «команде агентов» вводят владельца в заблуждение — он видит на экране Hub роли, которые Hermes не спрашивает. + +4. **Предложить путь связывания** профилей Hub с профилями Hermes и оценить его трудоёмкость: сопоставление по идентичности аккаунта (email из `id_token`), либо чтение профилей Hermes как источника, либо явная таблица соответствия. Решение принимает владелец — вам подготовить варианты с ценой каждого, не реализовывать молча. + +**Тесты:** вызов без определяемой роли уходит вниз, не тратя попыток роутера; вызов с определённой ролью маршрутизируется; отказ цепочки никогда не возвращается как ответ ассистента. + +## P0-01. Codex: обновление токена и безопасное переключение аккаунта + +Владелец прислал, как это делает Cockpit Tools после обновления, и просит так же. Их последовательность: + +``` +1. Прочитать данные аккаунта access_token · id_token · refresh_token +2. Проверить access_token действителен до 28.08, обновление не требуется +3. Проверить id_token истёк 5 дней назад — нужно обновление +4. Обновить данные входа полный набор обновлён и сохранён +5. Остановить прежний процесс безопасная остановка ChatGPT/Codex и app-server +6. Записать данные клиента +7. Синхронизировать настройки +8. Запустить клиент Codex +``` + +Ключевое в этой схеме: **токены проверяются по отдельности**, обновляется весь набор, и **клиент останавливается до подмены учётных данных, а не после**. + +Состояние у нас: + +- `codex_oauth.py` сохраняет `refresh_token` (строка 226), но **функции обновления не существует**. Для Antigravity есть `oauth.refresh_access_token`, для Codex — ничего. Протухший токен Codex означает полный повторный вход вместо тихого обновления. +- Раздельной проверки `access_token` и `id_token` нет. +- Остановки клиента Codex при смене аккаунта нет вовсе: подмена учётных данных под работающим процессом оставляет его со старыми. + +**Требуется:** + +1. Обновление токена Codex по `refresh_token`, с сохранением полного набора и понятной ошибкой, когда `refresh_token` отсутствует или отвергнут. +2. Раздельная проверка срока `access_token` и `id_token` с запасом по времени; в статусе профиля видно, что именно просрочено. +3. Переключение аккаунта как последовательность с остановкой клиента **до** записи учётных данных и запуском **после**. Шаги должны быть наблюдаемыми — интерфейс покажет их прогрессом (это часть B8), а от вас нужен backend, который эти шаги выполняет и сообщает о каждом. +4. Сбой на любом шаге не оставляет систему в промежуточном состоянии: либо аккаунт переключён полностью, либо всё вернулось к прежнему. + +**Тесты:** просроченный `access_token` при живом `refresh_token` обновляется без повторного входа; отсутствие `refresh_token` даёт понятную ошибку, а не молчаливый провал; прерывание на середине переключения не оставляет смешанных учётных данных. + +--- + ## P0-1. Квоты для OpenAI Codex и OpenCode Go Жалобы владельца: **«лимиты не подтягиваются, всё стоит Н/Д»** и **«у опенкода тоже нет лимитов»**. @@ -204,16 +281,19 @@ claude-sonnet-4-6, claude-opus-4-6-thinking, gpt-oss-120b-medium ## Критерии приёмки 1. Ни один файл зоны Codex не изменён. -2. На существующем конфиге без claude/grok после загрузки `find_free_slot` для обоих возвращает существующий профиль; десять профилей `antigravity` не изменены; проверено тестом. -3. Миграция идемпотентна и не теряет пользовательские правки и комментарии. -4. Квоты `openai-codex` и `opencode-go` приходят измеренными либо `None` с причиной; ни одного выдуманного числа; проверено тестом на обоих исходах. -5. Обнаружение моделей кэшируется, обновляется в фоне и не блокирует вызывающий поток при таймауте; проверено тестом с искусственной задержкой. -6. Новые профили не содержат выдуманных моделей; проверка конфигурации сообщает о несуществующих моделях в ролях владельца. -7. `/reinstall` либо работает с кодами возврата, либо удалён; предупреждение CS0219 при сборке отсутствует. -8. В отчёте назван конкретный способ доставки установщика владельцу. -9. Прогон **в обоих окружениях**; обе команды и оба результата в отчёте. -10. `ruff check .` чисто. Про релизный гейт: он **красный на `main` уже сейчас** (проверка 4 падает не по вашей вине). Указать в отчёте его состояние до и после ваших правок; ухудшать нельзя. -11. Отчёт: `START_HEAD`, `FINAL_HEAD`, `origin/main`, `git status`, точный `X passed / Y skipped / Z failed`. +2. Вызов без достоверно определённой роли уходит вниз, не тратя попыток роутера; отказ цепочки никогда не возвращается как ответ ассистента; правка `2d62d39` сохранена. +3. В контракте описана граница между учётными системами Hub и Hermes; подготовлены варианты связывания профилей с оценкой цены каждого. +4. Токен Codex обновляется по `refresh_token`; переключение аккаунта останавливает клиент до подмены учётных данных и не оставляет промежуточного состояния. +5. На существующем конфиге без claude/grok после загрузки `find_free_slot` для обоих возвращает существующий профиль; десять профилей `antigravity` не изменены; проверено тестом. +6. Миграция идемпотентна и не теряет пользовательские правки и комментарии. +7. Квоты `openai-codex` и `opencode-go` приходят измеренными либо `None` с причиной; ни одного выдуманного числа; проверено тестом на обоих исходах. +8. Обнаружение моделей кэшируется, обновляется в фоне и не блокирует вызывающий поток при таймауте; проверено тестом с искусственной задержкой. +9. Новые профили не содержат выдуманных моделей; проверка конфигурации сообщает о несуществующих моделях в ролях владельца. +10. `/reinstall` либо работает с кодами возврата, либо удалён; предупреждение CS0219 при сборке отсутствует. +11. В отчёте назван конкретный способ доставки установщика владельцу. +12. Прогон **в обоих окружениях**; обе команды и оба результата в отчёте. +13. `ruff check .` чисто. Про релизный гейт: он **красный на `main` уже сейчас** (проверка 4 падает не по вашей вине). Указать в отчёте его состояние до и после ваших правок; ухудшать нельзя. +14. Отчёт: `START_HEAD`, `FINAL_HEAD`, `origin/main`, `git status`, точный `X passed / Y skipped / Z failed`. ## Главное