Маршрутизация через Antigravity не работает ни для одного из шести аккаунтов при полностью валидных токенах: квоты по ним приходят настоящими через прямой HTTPS, а путь через CLI падает с AuthExpiredError. Причина найдена и проверена: agy читает <HOME>/.gemini/oauth_creds.json с шестью полями, включая id_token и scope. Hub пишет свой auth.json в другом месте и другой структурой, а id_token и scope теряет в двух местах — oauth.py:88-92 и profile_oauth.py:241-249. Ни один профиль их не хранит. Проверено и не сработало: подмена HOME на каталог профиля, сборка oauth_creds.json без id_token. Значит id_token обязателен. Владелец решил остаться на OAuth, прямой API отклонён. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
12 KiB
Задание A20: восстановить работу Antigravity через OAuth
Дата поступления
2026-08-24
База
Проверочный HEAD на момент выдачи: 0c325ae.
Ветка
antigravity/agy-oauth-credentials
Приоритет
Выше всего остального. Сейчас маршрутизация через Antigravity не работает ни для одного из шести аккаунтов — это десять профилей из двадцати двух и основной провайдер владельца.
Порядок работы с git
cd <каталог репозитория>; git fetch origin --prune; git status
git checkout main; git pull --ff-only origin main
git checkout -b antigravity/agy-oauth-credentials
git commit -m "..." <- сначала коммит
git push -u origin antigravity/agy-oauth-credentials
В конце — push и проверка git log --oneline -1 origin/antigravity/agy-oauth-credentials, git status чистый.
Прошлый раз работа была закоммичена, но не отправлена, и ветка в origin осталась пустой. Push без коммита и коммит без push одинаково бесполезны.
Решение владельца
Подключение остаётся через OAuth. Переход на прямой API отклонён. Задание — починить OAuth, а не обойти его.
Что установлено
Диагностика проведена целиком, повторять её не нужно.
Симптом
adapter.invoke(ag-w1) -> AuthExpiredError: agy error: authentication failed or timed out
agy models -> Error: Please sign in to view available models
При этом квоты по тем же аккаунтам приходят настоящими: ag-w2 — 37.4% недельного пула Claude/GPT, source=provider_api. То есть OAuth-токены живые и валидные. Не работает именно путь через CLI.
Причина
agy читает учётные данные из <HOME>/.gemini/oauth_creds.json. Формат — проверен по рабочей глобальной сессии владельца:
access_token str
refresh_token str
scope str
token_type str
id_token str (длина ~1200, это JWT)
expiry_date int (миллисекунды)
Hub же пишет <профиль>/auth.json в другом месте и в другой структуре:
token.access_token, token.refresh_token, token.expiry,
token.expires_at, token.token_type, email, auth_method, project_id
Файла oauth_creds.json в каталогах профилей нет ни у одного аккаунта. Поэтому agy, запущенный с подменённым HOME, не видит входа и отвечает «please sign in».
Чего не хватает и где это теряется
Проверено по всем шести профилям: ни один не хранит id_token и scope.
Теряются они в двух местах:
oauth.py:88-92—refresh_access_tokenвозвращает только четыре поля:
return {
"refresh_token": data.get("refresh_token") or refresh_token,
"access_token": data["access_token"],
"expires_at": _expires_at(data.get("expires_in")),
"token_type": data.get("token_type", "Bearer"),
}
id_token и scope приходят от Google в этом же ответе и просто отбрасываются.
profile_oauth.py:241-249— при первичном сохранении вauth_data["token"]кладутся толькоaccess_token,refresh_token,expiry. Ниid_token, ниscope, ниtoken_type.
Косвенное подтверждение, что поле должно быть: profile_manager.get_profile_status для Antigravity читает tokens.get("id_token") и вызывает extract_jwt_identity — код рассчитывает на id_token, которого поток никогда не сохранял.
Что уже проверено и не сработало
Чтобы вы не повторяли:
- Запуск
agy modelsс подменойHOME/USERPROFILEна каталог профиля — не помогает, файла с учётными данными там нет. - Сборка
oauth_creds.jsonиз имеющихся полей с пустымid_tokenи подставленнымscope— не помогает,agyпо-прежнему требует вход. Значит одногоaccess_tokenнедостаточно, иid_tokenскорее всего обязателен.
Правка ревьюера уже в main: discover_models теперь принимает profile_id и подменяет окружение, таймаут поднят с 10 до 60 секунд. Основание верное, но само по себе это симптом не лечит.
P0-1. Сохранять полный набор учётных данных
oauth.py:refresh_access_tokenвозвращаетid_tokenиscopeиз ответа Google наряду с остальным. Не терять их и при повторном обновлении — если Google не вернулid_tokenв ответе на refresh, сохранять прежний, а не затирать пустым.profile_oauth.py: при первичном сохранении класть вtokenвесь набор —access_token,refresh_token,id_token,scope,token_type,expires_at,expiry.- Запрашивать в OAuth те же scope, что запрашивает сам
agy. Если текущий набор уже,id_tokenможет не прийти вовсе — сверьте соscopeиз рабочей глобальной сессии (~/.gemini/oauth_creds.json, поле длиной ~150 символов).
Тест: после прохождения OAuth профиль содержит непустые id_token и scope; повторное обновление токена их не стирает.
P0-2. Писать oauth_creds.json в каталог профиля
При каждом сохранении и обновлении учётных данных Hub обязан класть в <профиль>/.gemini/oauth_creds.json файл ровно в формате agy:
expiry_date— миллисекунды, не секунды. У Hub хранитсяexpires_atв секундах, умножать на 1000;- запись атомарная, через временный файл и
os.replace. Это уже правило проекта:do_save_settingsтак и делает после того, как потеряла атомарность при переносе; - права на файл — как у остальных хранилищ учётных данных, секрет не должен стать доступен шире.
Тест в песочнице: сохранение профиля создаёт oauth_creds.json со всеми шестью полями; expiry_date в миллисекундах; обновление токена перезаписывает файл, а не плодит второй.
P0-3. Проверить, что заработало, — исполнением
Задание принимается только с доказательством:
agy modelsс подменённым окружением профиля возвращает непустой список — приложить вывод;- настоящий вызов модели через
adapter.invokeвозвращает ответ, а неAuthExpiredError— приложить; route_requestдля роли, ведущей на Antigravity, отрабатывает без ухода в резерв по причине авторизации.
Если после правки останется нужда в однократном повторном входе по каждому аккаунту (вероятно: у существующих профилей id_token не сохранён и взяться ему неоткуда) — сказать об этом прямо и описать порядок для владельца. Это законный исход: шесть аккаунтов один раз пройти мастер. Молча оставить шесть нерабочих профилей — нет.
P1-4. Обнаружение моделей после починки
Когда agy models заработает, доделать то, что осталось от A18 и не было сделано:
- кэш моделей на диске, переживающий перезапуск;
- обновление в фоне с таймаутом; таймаут не затирает прежний кэш;
- ручная кнопка обновления — владелец должен иметь возможность попробовать снова;
- при пустом кэше — «список моделей ещё не получен», без литеральных подстановок.
Сейчас set_model отклоняет любую модель, включая настоящую, потому что сравнивать не с чем. Владелец хочет поставить двум кодерам gemini-3.1-pro-high и не может.
Ограничения
- Ваши файлы:
oauth.py,router/profile_oauth.py,router/profile_manager.py,agy_subprocess.py,router/adapters/antigravity_adapter.py,model_discovery*,credentials.py. - Учётные данные не логировать. Ни токен, ни его часть, ни
id_tokenне должны попасть в журналы, в снапшот и в веб-API. Тест на отсутствие секретов в ответе уже есть — он должен продолжать проходить. - Десктоп и веб не ломать. На
mainсейчас 342 passed. - Тег
v0.1.1не создавать.
Критерии приёмки
- Ветка в
origin,git statusчист. - После OAuth профиль содержит непустые
id_tokenиscope; обновление токена их не теряет; проверено тестом. oauth_creds.jsonпишется в каталог профиля во всех шести полях,expiry_dateв миллисекундах, запись атомарная; проверено тестом в песочнице.- Приложен вывод
agy models, вернувший непустой список через профиль Hub. - Приложен успешный реальный вызов модели через
adapter.invoke. - Если нужен однократный повторный вход — порядок для владельца описан в отчёте.
- Секреты не попадают в журналы и в ответ веб-API; существующий тест на секреты проходит.
- Кэш моделей на диске, ручное обновление, таймаут не затирает кэш.
ruff check .чисто; релизный гейт не ухудшен.- Отчёт:
START_HEAD,FINAL_HEAD,origin/main,git status,X passed / Y skipped / Z failed.
Главное
Продукт создан ради маршрутизации между аккаунтами Antigravity, и именно она сейчас не работает — при полностью валидных токенах. Всё остальное подождёт.
Порядок сдачи
Передать точный FINAL_COMMIT_SHA. Сдано только после появления коммита в origin.