# Задание 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`.
