Reverse Engineering Antigravity Models Auth

thumbnail
date
Aug 10, 2026
slug
antigravity-models
status
Published
tags
AI
summary
Reverse engineering wire protocols, binary-searching system prompts, unmasking a secret identity filter, and bringing Gemini 3.6 Flash to Hermes Agent.
type
Post

The Mystery of the 429: How a Single Sentence Broke Google’s Free AI Models in Hermes Agent

Reverse engineering wire protocols, binary-searching system prompts, unmasking a secret identity filter, and bringing Gemini 3.6 Flash to Hermes Agent.

💡 TL;DR: Trying to use Google’s free Antigravity models (Gemini 3.1 Pro, Gemini 3.5 Flash) in Hermes Agent kept throwing 429 RESOURCE_EXHAUSTED errors, even with 100% quota remaining. The culprit wasn't rate limits or payload size—Google's API silently rejects requests whose system prompt contains the sentence "You are Hermes Agent, an intelligent AI assistant created by Nous Research". Neutralizing that single identity line instantly restored full access.
In this post, I cover how I uncovered the filter, sanitized JSON Schemas, fixed Hermes' dual provider resolution system, and wired up full support for the new Gemini 3.6 Flash with reasoning effort mapping.

1. The Dream: Free Frontier Models in Hermes

If you’ve used omp (Oh My Pi), you know the magic of Google Antigravity: direct, zero-cost access to Gemini 3.1 Pro, Gemini 3.5 Flash, Gemini 3.6 Flash, Claude 4.6 via Google, and GPT-OSS 120B.
I do most of my heavy daily coding inside Hermes Agent (by Nous Research). Naturally, I wanted the same free model access in Hermes. Fortunately, Hermes supports third-party provider plugins via antigravity-provider, an in-process bridge that translates OpenAI-formatted chat completions into Google’s Cloud Code Assist API (v1internal:streamGenerateContent).
I installed the plugin, set provider: antigravity, selected google-antigravity/gemini-3.1-pro, and ran a quick smoke test.
That’s when the headaches started.

2. Chapter 1: The Setup Bosses (Imports & Schemas)

Before hitting the main boss, I had to clear two preliminary hurdles.

Hurdle 1: The Invisible Plugin

On the first run, Hermes crashed before making a network call:
The plugin repository used a standard Python src/ layout (src/antigravity_provider), but Hermes' plugin loader generated an import shim that couldn't locate the package inside the virtual environment.
  • The Fix: Installing the package in editable mode (pip install -e ~/.hermes/plugins/antigravity-provider) permanently bound the module to Hermes’ venv, surviving any future hermes agy login shim regenerations.

Hurdle 2: Schema Indigestion

Once the plugin loaded, requests reached Google’s Cloud Code Assist endpoints—only to instantly bounce with 400 Bad Request validation errors:
Hermes exports rich, expressive JSON Schemas for its tools, using standard keywords like const, oneOf, anyOf, $ref, and additionalProperties. Google’s Gemini function declaration parser, however, demands strict, minimal schemas.
  • The Fix: I added a recursive schema sanitizer in transform.py to strip unsupported JSON Schema keywords before sending tool definitions upstream:

3. Chapter 2: The Ghost 429 Quota Error

With schemas sanitized, requests were finally formatted correctly. But now, every single attempt failed with a brick wall:
A standard HTTP 429 usually means one thing: you're out of quota.
Except I wasn't.
I opened omp on the exact same machine, running on the exact same Google OAuth accounts, and executed prompts against Gemini 3.5 Flash without a single hitch. Both accounts showed 100% free quota remaining.
Why would omp work flawlessly while Hermes failed every single time?

4. Chapter 3: Reverse Engineering omp

To find out what omp was doing differently, I dug into ~/.omp/ and inspected its compiled binary:
  1. Multi-Account Rotation: omp maintains four Google OAuth accounts inside SQLite (~/.omp/agent/agent.db), automatically rotating when one gets throttled.
  1. Wire Protocol Parity: omp talks to the exact same endpoint (https://daily-cloudcode-pa.googleapis.com/v1internal:streamGenerateContent?alt=sse) using the exact same request envelope (requestType: "agent", userAgent: "antigravity", ideType: "ANTIGRAVITY").
The wire protocol was 100% identical.
I extracted the OAuth tokens from omp's database and ran isolated test requests using Hermes' Python client:
  • Small Prompt ("Reply with exactly: OK"): HTTP 200 OK (worked on all 4 accounts across both endpoints).
  • Full Hermes Agent Prompt (19KB system prompt + 36 tool schemas ≈ 41KB): HTTP 429 RESOURCE_EXHAUSTED (failed on all 4 accounts).
Small prompts worked, but full agent prompts failed. That meant it wasn't an account-level quota issue. It was deterministic based on the request payload itself.

5. Chapter 4: The Rabbit Hole — Bisecting the System Prompt

Was it payload size?
I generated a dummy 30KB system prompt packed with 36 heavy tool definitions and sent it. HTTP 200 OK.
Size wasn't the issue. The failure was content-dependent.
I began binary-searching the real 19KB Hermes system prompt line by line, testing slices against the live API. Eventually, I narrowed the failure down from 19,000 characters to a single sentence:
If that sentence was present in the system prompt, Google's API returned HTTP 429 RESOURCE_EXHAUSTED. If I modified a single word, it succeeded.

The Bisect Matrix

Here is what happened when I tested variations of that sentence:
System Instruction Variation
API Result
"You are Hermes Agent."
200 OK
"You are an intelligent AI assistant created by Nous Research."
200 OK
"You run on Hermes Agent (by Nous Research)."
200 OK
"You are Hermes Agent, an intelligent AI assistant created by Nous Research"
429 EXHAUSTED
"You are Hermes Agent, an intelligent AI assistant created by X Corp"
200 OK
"You are a Helpful Agent, an intelligent AI assistant created by Nous Research"
200 OK

The Revelation 🤯

Google’s Cloud Code Assist API has an upstream filter that intercepts system instructions asserting a competing assistant's identity. When triggered, instead of returning a standard policy error, the API returns a masked 429 RESOURCE_EXHAUSTED.
omp never ran into this because it injects Google's own identity string ("You are Antigravity, a powerful agentic AI coding assistant designed by the Google Deepmind team...").
Hermes' prompt opens with "You are Hermes Agent, an intelligent AI assistant created by Nous Research"—meaning every single real Hermes prompt was getting caught in Google's identity filter.

The Fix

I added a light regex scrubber to transform.py:
The moment that scrub went in, the full 41.8KB prompt returned HTTP 200 OK. All of Hermes’ tool guidance, memory rules, and formatting instructions remained 100% intact—only the brand identity line was sanitized.

6. Chapter 5: The Dual Provider Resolution Architecture

With prompts working in one-shot mode (hermes -z), I tried switching models in the TUI (/model) and CLI (hermes model). Immediately, a new error popped up:
Why did -z work when model switching failed?
Hermes uses two different provider lookup functions:
  1. resolve_runtime_provider(): Used for execution. It queries the in-memory plugin registry where register_provider_profile() registers AntigravityProfile. This is why z worked.
  1. resolve_provider_full(): Used by hermes model, /model in the TUI, and switch_model(). It searches:
      • Config file (providers: in config.yaml)
      • Built-in provider aliases
      • Saved custom providers
      • models.dev catalog
Because resolve_provider_full() never checks the runtime plugin registry, passing explicit_provider="antigravity" returned None, crashing the model switcher.

The Fix

The error message prescribed the exact fix: add antigravity to ~/.hermes/config.yaml under providers::
To make this permanent for all new setups, I patched _select_model() in the plugin (antigravity_provider/hermes_plugin.py) to auto-populate this section whenever hermes agy select runs:

7. Chapter 6: Adding Gemini 3.6 Flash Support

With the foundation solid, I wanted to add support for Gemini 3.6 Flash.
However, sending a raw model ID of gemini-3.6-flash returned 404 NOT_FOUND. Google’s Cloud Code Assist API requires a reasoning effort suffix (-low, -medium, or -high) on the wire for 3.6 Flash:
  • gemini-3.6-flash-low
  • gemini-3.6-flash-medium
  • gemini-3.6-flash-high
I updated the plugin’s models.py to declare google-antigravity/gemini-3.6-flash in KNOWN_MODELS and map Hermes’ reasoning effort knob to the correct wire model ID:
Now, selecting google-antigravity/gemini-3.6-flash dynamically maps reasoning effort levels (low, medium, high) directly to Google's expected wire model identifiers.

8. Chapter 7: Usage & Analytics

Does Hermes track usage for Antigravity models?
Yes! Hermes logs all token usage into ~/.hermes/state.db under billing_provider='antigravity':
Running hermes insights cleanly aggregates these models alongside your paid providers.
📌 Note: The only difference between omp and Hermes here is that omp queries Google's internal fetchUsage API to render a live "remaining daily quota" meter. Hermes logs spent tokens per session locally.

9. Chapter 8: The Moment of Truth

With all fixes and features in place, I ran full end-to-end verification:

1. Terminal Smoke Tests

2. Programmatic Model Switching Test


© Adrian De Vera - 2026