Skip to content

feat(providers): proveedor Antigravity con cuota por modelo del IDE - #207

Merged
egdev6 merged 13 commits into
egdev6:mainfrom
eichhorn-micaela:feat/antigravity-provider
Oct 30, 2026
Merged

egdev6 merged 13 commits into
egdev6:mainfrom
eichhorn-micaela:feat/antigravity-provider

Conversation

@eichhorn-micaela

Copy link
Copy Markdown
Contributor

Issue relacionada

Issue: no hay issue previa (contribución de un proveedor nuevo).
ODD: no aplica.

Alcance

Nuevo proveedor Antigravity (IDE de Google): cuota del agente por modelo, la misma que muestra el panel de cuotas del IDE.

  • Antigravity no guarda una sesión reutilizable en disco (no hay un oauth_creds.json como en Gemini CLI) y su cuota es un grupo propio, distinto del de Gemini CLI. Por eso no sirve reutilizar el proveedor Gemini.
  • El proveedor consulta el servidor de lenguaje local que arranca el IDE: POST http://127.0.0.1:<puerto>/exa.language_server_pb.LanguageServerService/GetUserStatus (endpoint no oficial).
    • Token CSRF: argumento --csrf_token del proceso language_server lanzado con --app_data_dir antigravity. Así se descartan Windsurf y otros IDEs que usan el mismo binario. Sin el token, el servidor responde 401.
    • Puerto HTTP: línea listening on random port at N for HTTP de language_server.log, solo si pertenece al arranque del mismo pid.
    • La petición va únicamente a 127.0.0.1 y el token no se persiste.
  • Un medidor por modelo, con las variantes de esfuerzo (High/Medium/Low) agrupadas y el modelo más gastado primero. El plan sale de userTier.description (p. ej. «Google AI Pro»). La ventana (5 h o semanal) se deduce del tiempo que falta para el reinicio, porque la respuesta no la incluye.
  • Si el IDE está cerrado, el informe queda en Warning con instrucciones, sin error.
  • Integración: AntigravityConfig { enabled }, pestaña en Ajustes, tipos TS, visibilidad, dataKey, i18n, monograma AG, OFFICIAL_USAGE_URL y filas en los README.

Exclusiones: sin cuentas adicionales (en la máquina hay una sola sesión del IDE, así que no se añade a ACCOUNT_PROVIDERS). En Windows, el proceso se descubre con Get-CimInstance, sin ventana. Linux/macOS usan ps y la carpeta de configuración del SO, pero solo se probó en Windows.

Coordinación

  • Para ODD: N/A (no es trabajo ODD).

Contribuidor: @eichhorn-micaela
Evidencia de asignación o acuerdo: N/A

Aceptación y validación

  • Criterios cumplidos: con Antigravity 2.24.0 abierto en Windows 11, fetch real devolvió Ok, plan «Google AI Pro» y 5 medidores (Claude Opus 5.5, Claude Sonnet 5.5, GPT-OSS 120B, Gemini 3.8 Flash con ventana de 5 h; Gemini 4 Argon con ventana semanal).
  • Comprobaciones ejecutadas y resultados:
    • cargo test --locked: 335 OK (7 nuevos en providers::antigravity; base 328), 1 ignorado preexistente.
    • cargo fmt --all -- --check: limpio. cargo clippy --all-targets: sin avisos en el código nuevo.
    • npm run typecheck: limpio. vitest run --dir src: 731 OK.
    • node scripts/i18n-inventory.mjs --check: OK (inventario regenerado en un commit aparte).
    • npm run app:build: OK, genera Quorb_0.8.0_x64-setup.exe y el .msi.
  • Comprobaciones no ejecutadas y motivo: 4 tests de scripts/setup-signing.test.mjs fallan en local con EPERM al crear symlinks (Windows sin modo desarrollador). Son ajenos a este cambio. No se probó en Linux/macOS.
  • Bloqueos pendientes: ninguno. El endpoint es interno de Antigravity y puede cambiar.

Antigravity no deja una sesión reutilizable en disco y su cuota es un
grupo propio, distinto del de Gemini CLI. El proveedor consulta el
servidor de lenguaje local que arranca el IDE (GetUserStatus, no
oficial): token CSRF desde la línea de comandos del proceso lanzado con
--app_data_dir antigravity y puerto HTTP desde language_server.log del
arranque actual (mismo pid). La petición va solo a 127.0.0.1 y el token
no se guarda.

Un medidor por modelo agrupando las variantes de esfuerzo (High/Medium/
Low), el más gastado primero; plan desde userTier.description. La
ventana (5 h o semanal) se deduce del tiempo hasta el reinicio. Con el
IDE cerrado el informe queda en Warning con instrucciones.

Config AntigravityConfig { enabled } (sin cuentas adicionales), pestaña
en Ajustes, tipos TS, visibilidad, dataKey, i18n, monograma AG y filas
en los README.
@egdev6

egdev6 commented Oct 29, 2026

Copy link
Copy Markdown
Owner

@eichhorn-micaela muchas gracias por la contribución. El proveedor está bien planteado: viene desactivado por defecto, la consulta va solo a 127.0.0.1, el token no se guarda, el filtro por --app_data_dir antigravity descarta Windsurf y la integración cubre todas las piezas (fetch_all/fetch_one, tipos, dataKey, visibilidad, i18n y README).

Antes de activar la revisión y los workflows de CI necesitamos estos cambios:

1. Ventana mal deducida cerca del reinicio (window_for)

Hoy la ventana se deduce solo por el tiempo que falta para el reinicio: hasta 5 h se trata como ventana de 5 h y hasta 7 días como semanal. A una cuota semanal le llega un momento en que le quedan menos de 5 h, y a partir de ahí se clasifica como de 5 h. En esas últimas horas el medidor muestra una ventana y un ritmo de consumo incorrectos.

Como la respuesta no incluye la duración, hace falta una deducción que no dependa de un solo instante. Por ejemplo, recordar la ventana ya observada para cada modelo, o usar el resetTime que comparten los modelos del mismo grupo. Si no hay una señal fiable, es preferible dejar window_seconds: None antes que informar una ventana equivocada. En cualquier caso, añade un test para este borde: cuota semanal con menos de 5 h para el reinicio.

2. Linux y macOS sin probar

La ruta dirs::config_dir()/Antigravity/logs/language_server.log y el filtro de ps no se han verificado fuera de Windows. Como pide CONTRIBUTING.md, no podemos dar a entender que funciona en plataformas que no se han medido. Indica en el README (las dos versiones) y en la pestaña de Ajustes que, por ahora, solo está verificado en Windows.

3. Aviso de endpoints no documentados en el README

El párrafo que enumera las integraciones con endpoints no documentados nombra a NaN y z.ai, pero no a Antigravity, aunque la propia PR indica que GetUserStatus es interno y puede cambiar. Añádelo en README.md y README.es.md.

Cuando estén aplicados, aprobamos los workflows y pasamos a la revisión. Gracias de nuevo.

…instante

Una cuota semanal a la que le quedan menos de 5 h para el reinicio se
clasificaba como ventana de 5 h, con ventana y ritmo incorrectos en sus
últimas horas.

Ahora la ventana sale de señales fiables: más de 5 h hasta el reinicio
solo puede ser semanal; un reinicio ya visto conserva la ventana deducida
entonces (memoria en proceso por medidor); un reinicio que cambió y cae
dentro de 5 h es una ventana de 5 h; y los modelos con el mismo
resetTime exacto comparten ventana. Sin ninguna señal, window_seconds
queda en None.

Tests del borde semanal con menos de 5 h, del aprendizaje de 5 h por
reinicio cambiado, del grupo y de reinicios lejanos o pasados.

Refs: feedback de review en egdev6#207
… documentado

Indica en los README y en la pestaña de Ajustes que por ahora solo se verificó en Windows, y añade Antigravity (GetUserStatus interno) al aviso de endpoints no documentados.

Refs: feedback de review en egdev6#207
Conflicto en Settings.tsx: main movió los paneles de suscripción a componentes con catálogos ES/EN; la sección de Antigravity pasa a AntigravityPanel con sus claves en es.ts/en.ts. docs/i18n-inventory.md se toma de main y se regenera aparte.
@eichhorn-micaela

Copy link
Copy Markdown
Contributor Author

Gracias por la revisión. Apliqué los tres puntos:

  1. Ventana (window_for) — c5e584b. La ventana ya no se deduce de un solo instante:

    • si faltan más de 5 h para el reinicio, solo puede ser semanal;
    • si ya se vio ese mismo reinicio, se conserva la ventana deducida entonces (memoria en proceso por medidor);
    • si el reinicio cambió y el nuevo cae dentro de 5 h, es una ventana de 5 h;
    • los modelos con el mismo resetTime exacto comparten ventana.

    Sin ninguna de esas señales, window_seconds queda en None. Hay tests del borde que señalaste (semanal con menos de 5 h para el reinicio, con y sin historial), del aprendizaje de 5 h, del grupo y de reinicios lejanos o pasados.

  2. Solo Windows — 000ba3d. La nota está en README.md, en README.es.md y en la pestaña de Ajustes.

  3. Endpoints no documentados — 000ba3d. Antigravity (GetUserStatus interno) está añadido al párrafo de los dos README.

El inventario i18n está regenerado en 78392f4.

Además integré main (a4ee32b). Para resolver el conflicto en Settings.tsx seguí la migración de los paneles a los catálogos ES/EN: la sección pasa a AntigravityPanel, con sus claves en es.ts y en.ts. El inventario está regenerado otra vez en 626ca4d.

Verificación local tras el merge: cargo test --locked 380 OK (11 del proveedor), cargo fmt --check limpio, npm run typecheck limpio, vitest run --dir src 786 OK e i18n-inventory --check OK. Clippy (sin avisos nuevos) lo pasé antes del merge.

…aneles

Mismo patrón que el resto de paneles migrados en C1b (egdev6#217): español verbatim con locale es e inglés sin restos de español con locale en.

Refs: coordinación C1b / egdev6#207
@eichhorn-micaela

Copy link
Copy Markdown
Contributor Author

Sobre la coordinación con C1b: como #217 entró antes, el panel de Antigravity ya está migrado a los catálogos ES/EN (AntigravityPanel, claves en es.ts/en.ts, español literal y el nombre «Antigravity» sin traducir).

Para completar el patrón, en 8ff9070 añadí sus casos a Settings-panels-i18n.test.tsx:

  • con es, el español idéntico al anterior;
  • con en, el inglés sin restos de español.

El panel no usa SecretInput, así que la nota 2 no le afecta. El inventario i18n está regenerado en a3d85c6 (solo cambian los hashes del árbol).

Verificación: vitest run --dir src 788 OK, npm run typecheck limpio e i18n-inventory --check OK.

@egdev6

egdev6 commented Oct 30, 2026

Copy link
Copy Markdown
Owner

@eichhorn-micaela gracias por los cambios. Los avisos de Windows y del endpoint interno están resueltos, y la migración de AntigravityPanel a ES/EN incluye los tests del panel. Queda un borde en window_for, líneas 239–241: que cambie el reinicio y falten menos de 5 h no demuestra una ventana de 5 h si hubo una pausa entre consultas válidas.

Por ejemplo, todo en UTC: el 27 de octubre a las 15:37 se observa un reinicio semanal del 29 de octubre a las 18:37 y se aprende WEEK. Quorb sigue abierto, pero Antigravity permanece cerrado hasta el 5 de noviembre a las 15:37. El siguiente reinicio semanal es el 5 de noviembre a las 18:37, exactamente siete días después del anterior. Como cambió y quedan tres horas, Some(_) => Some(FIVE_HOURS) lo convierte en 5 h; después, la rama de reinicio idéntico conserva ese valor incorrecto.

¿Puedes añadir ese caso de salto semanal tras una pausa y hacer la inferencia conservadora cuando falte evidencia? En una transición ambigua es preferible None a aprender 5 h solo por el cambio de fecha. Los tests actuales cubren conservar un reinicio semanal conocido y aprender con observaciones cercanas, pero no este intervalo sin observaciones. Los workflows del head revisado siguen pendientes de autorización (action_required); sus resultados todavía no están validados por CI.

…nsultas

Un reinicio que cambia y cae dentro de 5 h solo demuestra una ventana de
5 h si la consulta anterior es reciente. Si el reinicio cambió, la
ventana nueva empezó después de esa consulta; una semanal acabaría al
menos 7 días después de ella y no podría quedarle ≤ 5 h antes de
7 días − 5 h. Ahora se guarda cuándo se vio cada reinicio y, tras una
pausa mayor (Antigravity cerrado), la transición es ambigua y queda en
None en vez de aprender 5 h.

Tests: el salto semanal tras una pausa (27 oct → 5 nov) del review y la
cota del intervalo a ambos lados del límite.

Refs: feedback de review en egdev6#207
@egdev6

egdev6 commented Oct 30, 2026

Copy link
Copy Markdown
Owner

Hola @eichhorn-micaela, gracias por la contribución. He revisado el commit 65d0b94 y recomiendo corregir estos dos puntos antes de fusionar:

  • P1 · El token CSRF puede salir de localhost. En antigravity.rs:73–76, el cliente reqwest compartido respeta HTTP_PROXY/ALL_PROXY. Si NO_PROXY no excluye loopback, envía X-Codeium-Csrf-Token al proxy aunque la URL sea 127.0.0.1. Lo reproduje con un proxy local y un token ficticio. Para garantizar que esta consulta sea exclusivamente local, usaría un cliente dedicado con .no_proxy() y redirecciones deshabilitadas.
  • P2 · El parser puede seleccionar el puerto de otro proceso. En antigravity.rs:299–310, buscar pid 123 también coincide con pid 1234. Además, si el bloque del PID buscado no tiene puerto HTTP, la búsqueda continúa por los arranques posteriores y puede tomar el puerto de otro PID. Reproduje ambos casos con el cuerpo exacto de http_port. Conviene validar el PID completo y terminar la búsqueda al encontrar el siguiente marcador de arranque.

Validación: las 13 pruebas del proveedor y 3 del modelo pasan en un harness aislado. Las tres pruebas adicionales que exigen aislamiento del proxy, coincidencia exacta del PID y delimitación del bloque fallan y reproducen los problemas anteriores. No ejecuté la suite completa ni hice una prueba con el IDE real. ¿Puedes añadir estas regresiones junto con las correcciones?

…del log de otros procesos

P1: la consulta usaba el cliente HTTP compartido, que respeta
HTTP_PROXY/ALL_PROXY; sin un NO_PROXY que excluya loopback, el token
X-Codeium-Csrf-Token llegaba al proxy aunque la URL fuese 127.0.0.1.
Ahora va con un cliente dedicado (local_client) con no_proxy(), sin
seguir redirecciones y con timeout propio.

P2: http_port buscaba «pid N» como subcadena (pid 123 coincidía con
pid 1234) y, si el bloque de ese pid no tenía puerto HTTP, seguía por
los arranques posteriores. Ahora compara el pid completo del marcador
de arranque y solo mira su bloque, hasta el siguiente «Starting…».

Regresiones (RED observado antes del arreglo): pid exacto, bloque
delimitado, proxy configurado que no recibe la consulta y redirección
que no se sigue, con servidores de prueba en loopback.

Refs: feedback de review en egdev6#207
@egdev6

egdev6 commented Oct 30, 2026

Copy link
Copy Markdown
Owner

Hola @eichhorn-micaela, he revisado el head 164213c y no he encontrado nuevos defectos bloqueantes. Quedan resueltos los puntos anteriores sobre el aislamiento del proxy y las redirecciones, la selección del puerto por PID y la inferencia de ventanas tras pausas largas.

En la comprobación local pasan los 17 tests de Antigravity, los 788 tests del frontend, TypeScript y el inventario i18n. No he probado el IDE real en Windows ni ejecutado la suite Rust completa; tampoco pude comprobar el formato porque el entorno no tiene rustfmt. Nos encargamos de autorizar los workflows y completar la aprobación antes de fusionar.

¡Muchas gracias por aportar el proveedor Antigravity y por atender las revisiones con sus pruebas de regresión!

@egdev6
egdev6 merged commit a0bc5ed into egdev6:main Oct 30, 2026
5 checks passed
@eichhorn-micaela

Copy link
Copy Markdown
Contributor Author

Gracias por la revisión. Los dos puntos eran correctos y están corregidos en 1d7c4c8, con sus regresiones. Antes de arreglarlos vi fallar cada test por el motivo que describiste.

P1 · Proxy. La consulta ya no usa el cliente compartido. Va con un cliente dedicado (local_client): .no_proxy(), redirecciones deshabilitadas (Policy::none()) y timeout propio. fetch ignora el cliente compartido y lo documenta.

  • csrf_token_never_goes_through_a_proxy: cliente con un proxy configurado, que es el mismo caso que HTTP_PROXY/ALL_PROXY sin NO_PROXY para loopback. El servidor de loopback recibe el token y el proxy no recibe nada. Antes respondía el proxy (502).
  • local_query_does_not_follow_redirects: un 307 hacia otro puerto de loopback se devuelve tal cual y el destino no recibe nada. Antes se seguía.

Probé el proxy configurándolo en el builder, no con variables de entorno, para no mutar el entorno global en tests que corren en paralelo. no_proxy() desactiva ambos.

P2 · Puerto. http_port ahora parsea el pid completo del marcador «Starting language server process with pid N» y solo recorre el bloque de ese arranque, hasta el siguiente marcador.

  • port_lookup_matches_the_whole_pid: pid 123 ya no coincide con pid 1234.
  • port_lookup_stops_at_the_next_start: si el bloque del pid no tiene puerto HTTP, devuelve None en vez de tomar el del arranque siguiente.

Verificación

  • cargo test --locked: 386 OK (17 del proveedor). Los tests del proveedor pasaron en 10 de 10 corridas repetidas.
  • cargo fmt --check limpio, clippy sin avisos nuevos e i18n-inventory --check OK (regenerado en 164213c, solo hashes).
  • Con el IDE real (Antigravity 2.24, Windows 11), fetch devuelve Ok, plan «Google AI Pro» y los 5 modelos.

sosan added a commit to sosan/quorb that referenced this pull request Oct 30, 2026
…nes) en feat/wsl-adapter

Trae a la rama de la PR el main vigente de egdev6/quorb (f3897bc, incluye
egdev6#207 y egdev6#219). docs/i18n-inventory.md se regenera desde el árbol fusionado
en un commit aparte para que el header refleje el head real.

Co-Authored-By: Claude Code <noreply@anthropic.com>
sosan added a commit to sosan/quorb that referenced this pull request Oct 30, 2026
1. shell_strategy no confunde error WSL con ausencia de Bash:
   `control_raw` devuelve el output crudo, así que se clasifica con
   `errors::classify`: si la salida contiene un token WSL reconocido
   (p. ej. WSL_E_DISTRO_NOT_FOUND porque la distro desapareció entre la
   enumeración y la sonda), se propaga el error real en vez de caer a
   `ShPosix` como estrategia válida. El fallback a `ShPosix` queda solo
   para el caso normal `command -v` sin bash (salida vacía, sin token).
   Regresión: shell_strategy_propagates_wsl_distro_loss_not_sh_posix.

2. kill_remote_process no escala tras observar la muerte: si una sonda
   `kill -0` ve el PID muerto, se rompe y NO se vuelve a comprobar ni se
   envía KILL (un `kill -0` posterior podría devolver 0 porque el PID fue
   reutilizado por otro proceso — señalizar al proceso equivocado). La
   identidad de la instancia se protege observando la desaparición, no
   volviendo a preguntar por el número. Test determinista
   case_disappears_then_reappears: PID que desaparece y reaparece con
   otra identidad → no recibe KILL.

3. session_path usa semántica del GUEST (POSIX), no del host Windows:
   `Path::is_absolute()` en Windows devuelve false para `/home/u/...` y
   true para `C:\...`, justo al revés del guest. Nuevo helper
   `is_guest_absolute` (empieza por `/`) y tests: una ruta Windows
   explícita se rechaza en modo WSL, y `/etc/passwd` como relativa del
   proveedor se rechaza aunque en Windows no sea "absoluta".

docs/i18n-inventory.md regenerado tras integrar main del upstream
(incluye egdev6#207 y egdev6#219) y los cambios de este commit.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants