docs(task): A22 — вход в Antigravity силами самого agy

A20 закрыто как неверно поставленное. Синтез oauth_creds.json из
OAuth-потока Hub не работает в принципе: проверены и закрыты пять
гипотез (полнота полей, срок токенов, тот же OAuth-клиент, тот же
scope, согласованность активного аккаунта), а решающий опыт показал,
что agy отказывает даже РАБОЧИМ глобальным учётным данным владельца,
положенным в подменённый HOME. Ошибка в гипотезе автора задания.

Полезное следствие разведки: agy уважает подмену HOME — в каталогах
профилей лежат созданные им же файлы. Изоляция работает, не работал
только синтез.

A22 строит вход через родной механизм agy в окружении профиля. Первым
пунктом — разведка: есть ли неинтерактивный вход, что agy выводит,
как определить завершение. Установлено, что без аргументов это TUI:
в пайп ничего не пишет и виснет.

Приёмка только с тремя доказательствами исполнением: непустой вывод
agy models, успешный реальный вызов, route_request без ухода в резерв.

Плюс проверка побочной находки: в глобальном google_accounts.json
активен один аккаунт, а токен принадлежит другому — Hub мог писать
мимо каталога профиля и сломать владельцу обычный agy.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Hermes Team 2026-08-23 23:38:46 +07:00
parent bb4f6df67a
commit f514b3e6de

View file

@ -0,0 +1,146 @@
# Задание A22: вход в Antigravity силами самого `agy`
## Дата поступления
2026-08-24
## База
Проверочный HEAD на момент выдачи: **`bb4f6df`**.
## Ветка
`antigravity/agy-native-login`
## Приоритет
Выше всего остального. Маршрутизация через Antigravity не работает ни для одного из шести аккаунтов — это десять профилей из двадцати двух и основной провайдер владельца.
---
## Порядок работы с git
```
cd <каталог репозитория>; git fetch origin --prune; git status
git checkout main; git pull --ff-only origin main
git checkout -b antigravity/agy-native-login
git commit -m "..." <- сначала коммит
git push -u origin antigravity/agy-native-login
```
**В `main` напрямую не пушить.** Работа A20 ушла в основную ветку минуя ревью; в тот раз обошлось, но правка, ломающая сборку, попала бы к владельцу без проверки. Ветка существует именно для этого.
В конце — push и проверка `git log --oneline -1 origin/antigravity/agy-native-login`, `git status` чистый.
---
## Задание A20 закрыто как неверное. Ошибка в постановке, не в исполнении
A20 требовал сохранять `id_token` и `scope` и писать `<профиль>/.gemini/oauth_creds.json` в формате `agy`. **Это сделано, и сделано аккуратно** — атомарная запись через временный файл, поля не затираются при обновлении, 350 passed, ruff чисто.
**Но задача не решилась**, и подход в принципе не может её решить. Формулировал задание ревьюер, ошибка в гипотезе его.
Проверено исполнением, каждая гипотеза закрыта:
| Проверено | Результат |
|---|---|
| Все шесть полей в `oauth_creds.json` | на месте — отказ |
| Токены не просрочены | живы ещё 43 минуты — отказ |
| Тот же OAuth-клиент (`aud`, `azp`) | совпадают полностью — отказ |
| Тот же набор разрешений | 6 из 6, различий нет — отказ |
| Активный аккаунт согласован с владельцем токена | согласован — отказ |
И решающий опыт: в подменённый `HOME` положены **рабочие глобальные** учётные данные владельца, действительные ещё 40 минут, — `agy` отказал и им.
**Вывод: `agy` не принимает учётные данные, выпущенные не им самим.** Ни в каком расположении, ни с какими полями. Синтезировать их из OAuth-потока Hub нельзя.
### Что при этом выяснилось полезного
**`agy` уважает подмену `HOME`.** В каталогах профилей лежат его собственные файлы, созданные им же: `.gemini/antigravity-cli/conversation_summaries.db` и `installation_id` с отметкой 23 августа 12:19. Изоляция профилей работает — не работал только синтез учётных данных.
Значит путь один: **вход должен выполнять сам `agy`, в окружении нужного профиля.**
---
## P0-1. Разведка перед реализацией
Не повторяйте ошибку A20 — сначала проверьте, потом стройте.
Установлено: `agy` без аргументов — **интерактивный TUI**. Запущенный с перехваченными потоками он ничего не выводит в пайп и виснет; ему нужен настоящий терминал.
**Прежде чем писать код, выясните и напишите в отчёте:**
1. Есть ли у `agy` неинтерактивный вход — флаг, подкоманда, переменная окружения. В `agy --help` подкоманд входа нет (`agent`, `agents`, `changelog`, `help`, `install`, `mcp`, `models`, `plugin`, `plugins`, `update`), но проверьте `agy help <подкоманда>` и документацию.
2. Пишет ли `agy` при входе что-нибудь пригодное для автоматики — URL, код устройства — в файл или в stderr.
3. Как надёжно определить, что вход завершился: появление `oauth_creds.json` в каталоге профиля, изменение `google_accounts.json`, что-то ещё.
Если неинтерактивного входа нет — так и напишите. Это законный результат, и он определяет всё остальное.
## P0-2. Вход по профилям
Опираясь на разведку, сделать в Hub подключение аккаунта Antigravity **через родной вход `agy`**:
- окружение подменяется на каталог профиля (`USERPROFILE`, `HOME`, `HOMEPATH`) — механизм уже есть в `antigravity_adapter.get_profile_env_dir`, он проверен и работает;
- если неинтерактивного входа нет — открывать **видимое консольное окно** с этим окружением, чтобы владелец прошёл вход сам. Это честный путь: он ровно так и делал вручную;
- Hub дожидается завершения, определяя его признаком из разведки, и сообщает результат в интерфейсе;
- **шесть аккаунтов подряд**: мастер должен вести по ним, а не заставлять повторять всё руками для каждого.
**Ограничение по безопасности, без исключений.** Учётные данные вводит владелец в окне `agy`. Hub их не перехватывает, не читает из потоков, не логирует и не пересылает. Задача Hub — подготовить окружение и дождаться результата.
## P0-3. Веб-интерфейс должен сказать правду
На сервере без экрана консольный вход невозможен. Это уже зафиксировано в контракте: Antigravity и Claude требуют redirect на localhost и на headless не работают.
В вебе для Antigravity показывать не кнопку подключения, а объяснение и обходной путь: пройти вход на десктопе, либо пробросить порт по SSH. **Неработающая кнопка недопустима** — правило то же, что и с зелёным «Работает» на нерабочем аккаунте.
## P0-4. Проверить глобальную сессию владельца
Побочная находка, требует проверки: в глобальном `~/.gemini/google_accounts.json` активным записан `victor.trushenko@gmail.com`, а токен в `~/.gemini/oauth_creds.json` принадлежит `ochenstarik@gmail.com`.
Похоже, Hub где-то пишет **мимо каталога профиля**, в глобальное расположение, и мог сломать владельцу обычный `agy`.
Найти, откуда это, и убедиться, что Hub не трогает глобальные учётные данные ни при каких обстоятельствах. **Тест обязателен:** сохранение и обновление профиля не изменяет ни одного файла в `~/.gemini`.
## P0-5. Доказательство
Задание принимается **только с подтверждением исполнением**. Приложить:
1. вывод `agy models`, вернувший непустой список, через профиль Hub;
2. успешный реальный вызов модели через `adapter.invoke` — ответ модели, а не `AuthExpiredError`;
3. `route_request` на роли, ведущей на Antigravity, отработавший без ухода в резерв по причине авторизации.
Без этих трёх пунктов приёмки не будет. A20 был написан правильно по букве задания и всё равно не работал — цена ненадёжной гипотезы уже заплачена один раз.
## P1-6. Обнаружение моделей — после починки входа
Когда `agy models` заработает, доделать пропущенное в A18: кэш моделей на диске, переживающий перезапуск; фоновое обновление с таймаутом, не затирающим кэш; ручная кнопка обновления; при пустом кэше — «список ещё не получен» без литеральных подстановок.
Сейчас `set_model` отклоняет **любую** модель, включая настоящую, потому что сравнивать не с чем. Владелец хочет поставить двум кодерам `gemini-3.1-pro-high` и не может.
---
## Ограничения
- Параллельно идёт **A21** (четыре экрана веб-интерфейса). Его зона: `router/web/**` целиком. **Не заходить туда**, кроме честного объяснения для Antigravity по P0-3 — согласовать эту правку в отчёте.
- Ваши файлы: `oauth.py`, `router/profile_oauth.py`, `router/profile_manager.py`, `agy_subprocess.py`, `router/adapters/antigravity_adapter.py`, `model_discovery*`, `credentials.py`, `router/ui/add_account_wizard.py`.
- Учётные данные не логировать и не отдавать наружу; существующий тест на секреты должен продолжать проходить.
- Десктоп и веб не ломать. На `main` сейчас 350 passed, 2 skipped.
- Тег `v0.1.1` не создавать.
## Критерии приёмки
1. Ветка в `origin`, в `main` напрямую не пушилось, `git status` чист.
2. В отчёте есть результат разведки по трём вопросам P0-1.
3. Подключение аккаунта Antigravity проходит через родной вход `agy` в окружении профиля.
4. Мастер ведёт по нескольким аккаунтам подряд.
5. Hub не перехватывает и не логирует учётные данные.
6. **Приложен непустой вывод `agy models`** через профиль Hub.
7. **Приложен успешный реальный вызов модели.**
8. **Приложен `route_request`, не ушедший в резерв по авторизации.**
9. Тест: сохранение и обновление профиля не изменяет ни одного файла в `~/.gemini`.
10. В вебе Antigravity показан с объяснением и обходным путём, а не нерабочей кнопкой.
11. `ruff check .` чисто; релизный гейт не ухудшен.
12. Отчёт: `START_HEAD`, `FINAL_HEAD`, `origin/main`, `git status`, `X passed / Y skipped / Z failed`.
## Главное
Продукт создан ради маршрутизации между аккаунтами Antigravity. Она не работает при полностью валидных токенах, и обойти это синтезом учётных данных уже пробовали — не выходит. Остаётся дать `agy` войти самому, а Hub должен это организовать и не мешать.
## Порядок сдачи
Передать точный `FINAL_COMMIT_SHA`. Сдано только после появления коммита в `origin`.