Skip to main content

antigravity-native-e2e-dev

Spin up a live local Omnigent server + runner and exercise the native Antigravity (agy) TUI harness (antigravity-native) end-to-end — launch the real `agy` CLI via `omnigent antigravity`, drive turns through the web UI, smoke-test, and bug-bash. Load when developing, testing, or debugging the antigravity-native harness (omnigent/inner/antigravity_native_executor.py, omnigent/antigravity_native.py, antigravity_native_bridge.py, antigravity_native_rpc.py, antigravity_native_reader.py, antigravity_native_launch.py) or its agy launch / RPC mirror / tmux delivery / OAuth / MCP-relay behavior. NOT the in-process `antigravity` Gemini SDK harness.

Source facts

Repository
omnigent-ai/omnigent
Last source activity
October 2, 2026 at 16:35
Detected SKILL.md language
English
Stars
10,745
Forks
1,733

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
antigravity-native-e2e-dev
description
Spin up a live local Omnigent server + runner and exercise the native Antigravity (agy) TUI harness (antigravity-native) end-to-end — launch the real `agy` CLI via `omnigent antigravity`, drive turns through the web UI, smoke-test, and bug-bash. Load when developing, testing, or debugging the antigravity-native harness (omnigent/inner/antigravity_native_executor.py, omnigent/antigravity_native.py, antigravity_native_bridge.py, antigravity_native_rpc.py, antigravity_native_reader.py, antigravity_native_launch.py) or its agy launch / RPC mirror / tmux delivery / OAuth / MCP-relay behavior. NOT the in-process `antigravity` Gemini SDK harness.
# Antigravity native harness: end-to-end dev & testing (local server/runner) The `antigravity-native` harness wraps the **real Antigravity `agy` TUI** (the `agy` CLI, installed from `antigravity.google/cli/install.sh`). `omnigent antigravity` ensures a host daemon, the daemon-spawned **runner** launches `agy` in a runner-owned **tmux** terminal, and your TTY attaches to it. This is **not** the in-process `antigravity` Gemini-SDK harness — that one runs `google-antigravity` with a Gemini *API key*; this one drives the OAuth-only `agy` CLI and mirrors it over **connect-RPC**. This skill is the proven recipe for running it **for real against a live local server + runner** — not just the unit tests. > Like the other native harnesses, the runner imports from your **current > checkout**, so testing here exercises exactly the code you're on. (CWD/venv > selects the code, not `PYTHONPATH`.) ## What actually runs where ``` your TTY ── (attach / pexpect) ──► omnigent antigravity (CLI, local) │ ensures ▼ host daemon ──► local Omnigent server (AP) │ spawns ▲ ▼ connect-RPC │ HTTP runner ── launches ──► agy (TUI, in tmux) │ │ ├── write path: type web turns into the TUI │ (tmux bracketed paste → real USER_INPUT step) └── read path: RPC read driver mirrors agy's trajectory steps back into the session ``` Three transports, easy to confuse: 1. **Write path = typing into the TUI.** Every web/mobile turn is *typed* into the agy pane via tmux (`inject_user_message_via_tui`), creating a real `CORTEX_STEP_TYPE_USER_INPUT` step on the **same** cascade the TUI shows (#1156/#1158). It is **not** delivered over `SendUserCascadeMessage` (that headless RPC path was retired; the `antigravity_native.py` module header still says "delivered via the RPC" — that's stale doc-lag, the executor is authoritative). 2. **Read path = RPC.** `antigravity_native_reader` polls/streams agy's connect-RPC trajectory steps and mirrors them into the Omnigent session. 3. **Control = RPC.** Interrupt is `CancelCascadeSteps`; a tool/permission prompt is answered via `HandleCascadeUserInteraction` (surfaced as an Omnigent elicitation). ## Prerequisites (check these first) 1. **You're on the branch you want to test**, running from that checkout (`.venv/bin/omnigent` / `.venv/bin/python` from this repo). 2. **The `agy` CLI is on PATH** (or at `~/.local/bin/agy`) — the harness can't launch without it: ```bash which agy || ls -l ~/.local/bin/agy agy --version # install if missing (shell installer, NOT npm): # curl -fsSL https://antigravity.google/cli/install.sh | bash # then restart shell .venv/bin/python -c "from omnigent.onboarding.harness_readiness import harness_is_configured; print('antigravity-native ready:', harness_is_configured('antigravity-native'))" ``` 3. **`agy` is signed in (OAuth).** agy is **OAuth-only** — it has no `agy login`; you authenticate by running bare `agy` once and completing the browser sign-in. It **ignores `GEMINI_API_KEY`** (API-key auth belongs to the separate `antigravity` SDK harness). Verify (no secrets printed): ```bash .venv/bin/python -c "from omnigent.onboarding.gemini_auth import gemini_login_detected; print('agy oauth token present:', gemini_login_detected())" agy models # exits 0 and lists models only when signed in; else 'Please sign in' ``` `False` / non-zero → run `agy` once and sign in. agy's token lives under `~/.gemini` (`oauth_creds.json` on macOS through 1.0.10, `antigravity-cli/antigravity-oauth-token` on Linux); agy 1.1.7+ on macOS writes no token file and keeps the credential in the Keychain, which is why `gemini_login_detected()` falls back to `agy models` there. 4. **`tmux` is on PATH.** The agy terminal is a runner-owned tmux pane; the CLI attaches to it and the executor drives it via `tmux send-keys` (`_preflight_local_tools` hard-fails without tmux). 5. **Network egress to Google's Antigravity backend.** A turn that hangs / fails to connect on a locked-down host is usually egress, not a harness bug. > No `node` and no provider/gateway config are needed here (unlike pi/cursor > native): agy is a self-hosted binary and auth is the inherited Google OAuth. ## Step 1 — start a local server (real server + runner) ```bash cd /path/to/omnigent .venv/bin/omni server --background # detached managed server on a free loopback port .venv/bin/omni server status # prints the URL, e.g. http://127.0.0.1:6767 SERVER=http://127.0.0.1:6767 # use the printed URL below curl -s "$SERVER/health" # {"status":"ok"} ``` (`omnigent antigravity --server ""` also auto-spawns a persistent local server and uses it — handy for a one-shot manual run, but a known `$SERVER` URL is better for scripted API observation below.) ## Step 2 — launch the agy terminal against the local server `omnigent antigravity` **attaches an interactive TUI**, so run it where you can hold it open. Two patterns: **A. Background terminal (recommended for scripted drives).** Launch in one terminal, drive/observe from another: ```bash .venv/bin/omnigent antigravity --server "$SERVER" 2>&1 # attaches the agy TUI; leave it running # add a model: --model gemini-2.5-pro ; pass-through agy args go at the end ``` It prints `Web UI: <url>` and a resume hint to stderr — grab the conversation id (the `…/c/<conv_…>` segment) for the API calls below: ```bash CONV=conv_xxxxxxxx # from the "Web UI:" line / resume hint ``` **B. PTY driver (fully automated).** Drive it under `pexpect` like the `claude-native-e2e-test` skill's `cuj_driver.py`: spawn `omnigent antigravity --server <url>` in a PTY with `cwd=<checkout>`, capture the conv id from the printed URL, then drive/poll the API, then **tear down the whole process tree** (see Teardown — a pexpect Ctrl-C only *detaches* tmux). > The runner **owns** the agy terminal: binding a runner auto-creates the > antigravity terminal for the session, and the CLI *reattaches* rather than > launching its own. Don't hand-launch a second `agy` against the same session — > a double launch 500s and clobbers the runner's bridge state (web-turn injection > then fails "bridge state is missing"). ## Step 3 — drive a turn (and smoke-test) **Via the web path (exercises `AntigravityNativeExecutor`).** Post a user message to the running session; the runner routes it to the harness, whose `_deliver` types it into the agy TUI (real `USER_INPUT` step): ```bash curl -s -X POST "$SERVER/v1/sessions/$CONV/events" \ -H 'content-type: application/json' \ -d '{"type":"message","data":{"role":"user","content":[{"type":"input_text","text":"Reply with exactly the single word: PONG"}]}}' ``` Then **observe** the mirrored transcript (the RPC read driver posts agy's steps back): ```bash sleep 25 curl -s "$SERVER/v1/sessions/$CONV/items" | python -m json.tool | tail -40 ``` A healthy run shows your `user` message **and** a non-empty `assistant` reply (`PONG`) mirrored into the session — proving the full stack: server → runner → executor → tmux paste → agy turn → connect-RPC read driver → transcript mirror. You'll also see the prompt + reply render in the attached agy TUI (parity is the whole point of the TUI-typing write path). - **Type-driven smoke:** instead of the POST, type a prompt directly in the attached agy TUI and confirm it answers + mirrors to `…/items`. - **Model:** select a model with agy's TUI `/model`; the next web turn echoes that choice (the executor reads it from the latest `USER_INPUT` step). ## Inspect the bridge (debugging) Per-session bridge state lives under a hashed dir (keyed by *bridge id*, which defaults to the Omnigent conversation id): ```bash .venv/bin/python -c "from omnigent.antigravity_native_bridge import bridge_dir_for_bridge_id as d; print(d('$CONV'))" # ~/.omnigent/antigravity-native/<sha256(bridge_id)[:32]>/ # state.json <- {session_id, conversation_id (agy's real UUID once minted), active_turn_id} # tmux.json <- {socket_path, tmux_target} the executor types into (send-keys) # bridge.json <- token for the Omnigent MCP relay (sys_* tools) # agy-home/.gemini/... <- per-session ISOLATED HOME: a COPY of your OAuth token # + onboarding markers + config/mcp_config.json (relay) ``` Key facts: - agy mints its **own** UUID cascade; a fresh launch seeds an `agy_conv_*` **placeholder** until cold-start `StartCascade`s the real id and writes it to `state.json` (and PATCHes it as `external_session_id`). RPC calls against a placeholder are skipped — "not ready yet". - The **isolated HOME** (`agy-home/`) is why your real `~/.gemini` is never touched: the relay's `mcp_config.json` and agy's per-session state live there. agy's `/mcp` panel should show `✓ omnigent` with the `sys_*` tools. - Env vars: `HARNESS_ANTIGRAVITY_NATIVE_BRIDGE_DIR`, `HARNESS_ANTIGRAVITY_NATIVE_REQUEST_SESSION_ID`. ## Targeted scenarios | Goal | How | |------|-----| | Web→TUI delivery | POST a message (Step 3); confirm it renders in the agy TUI AND mirrors to `…/items` | | Native tools (shell/edit/read) | prompt agy to create→read→edit a file + run a command; confirm it touches disk | | Omnigent MCP relay (`sys_*`) | in the agy TUI run `/mcp` → expect `✓ omnigent`; prompt agy to `sys_session_list` / spawn a sub-agent | | Permission elicitation | with a tool that needs approval, agy's `request-review` surfaces as an **Omnigent elicitation** (interaction bridge); answer it in the web UI and confirm the tool runs | | Interrupt | mid-turn, hit stop in the UI → `CancelCascadeSteps` (RUNNING cascades only; a step WAITING on an interaction is unblocked by a DENY, not cancel) | | Model echo | `/model` in the TUI, then a web turn — confirm the new model is used (latest `USER_INPUT` step's `planModel`) | | Resume | stop, `omnigent antigravity --server "$SERVER" --resume "$CONV"`; `--resume` (no value) opens the antigravity-native picker | | Concurrency / leaks | drive several sessions; sweep for orphaned `agy` / tmux after teardown | ## Gotchas (these cost real time) 1. **It's a TUI, not `omni run`.** Use `omnigent antigravity`. The executor only delivers into the live agy pane — agy must be running (attached) for a turn to process. 2. **`config.yaml`'s `server:` defaults to a remote server.** Always pass `--server "$SERVER"` (or `--server ""` for local). If a *local* server rejects `antigravity-native`, it's stale — restart it from your checkout (allowlist: `omnigent/spec/_omnigent_compat.py`). 3. **OAuth-only.** agy ignores `GEMINI_API_KEY`; if `agy models` says "sign in", no web turn will get a real answer. Run bare `agy` once first. 4. **tmux must be reachable from the CLI process** for the direct attach; the executor's send-keys run on the runner side against the advertised socket. 5. **Isolated HOME.** Don't expect your real `~/.gemini` to change — agy runs under `<bridge_dir>/agy-home`. Look there (and `~/.gemini/antigravity-cli` for agy's own conversation store) when debugging. 6. **Don't double-launch agy** for a session — the runner owns the terminal (see Step 2). 7. **Turns take ~20–120s** — wrap scripted waits/`timeout` generously. 8. **Never print/echo the OAuth token.** Use the boolean/`agy models` probes. ## Code & tests - **Executor (write path — types into the TUI):** `omnigent/inner/antigravity_native_executor.py` - **Harness wrap (`harness: antigravity-native`):** `omnigent/inner/antigravity_native_harness.py` - **CLI launch / daemon-runner / tmux attach:** `omnigent/antigravity_native.py` (`run_antigravity_native`); CLI command `antigravity(...)` in `omnigent/cli.py` - **agy argv / auth-mode / permission flag:** `omnigent/antigravity_native_launch.py` - **Bridge (state, tmux delivery, isolated HOME, MCP relay):** `omnigent/antigravity_native_bridge.py` - **connect-RPC client (port discovery, send/cancel/interaction):** `omnigent/antigravity_native_rpc.py` - **RPC read driver (trajectory mirror):** `omnigent/antigravity_native_reader.py` - **Steps / interactions / audit:** `omnigent/antigravity_native_steps.py`, `omnigent/antigravity_native_interactions.py`, `omnigent/antigravity_native_audit.py` - **OAuth detection:** `omnigent/onboarding/gemini_auth.py` - **Design/plan docs:** `docs/antigravity-native-rpc-core-design.md`, `docs/antigravity-native-rpc-core-plan.md` ```bash .venv/bin/python -m pytest \ tests/harnesses/antigravity_native/test_antigravity_native.py \ tests/harnesses/antigravity_native/test_antigravity_native_bridge.py \ tests/harnesses/antigravity_native/test_antigravity_native_launch.py \ tests/harnesses/antigravity_native/test_antigravity_native_rpc.py \ tests/harnesses/antigravity_native/test_antigravity_native_reader.py \ tests/harnesses/antigravity_native/test_antigravity_native_steps.py \ tests/harnesses/antigravity_native/test_antigravity_native_interactions.py \ tests/harnesses/antigravity_native/test_antigravity_native_audit.py \ tests/inner/test_antigravity_native_executor.py -q ``` ## Bug-bash (fan out) Stress the harness against the same `$SERVER`: the web→TUI delivery path (lost / duplicated turns, the attended-TUI paste race), the RPC read mirror (does every agy step reach `…/items`? duplicates after a reader restart?), the MCP relay (`sys_*` reachable + gated), permission elicitations, interrupt (`CancelCascadeSteps`) vs. a WAITING-on-interaction step, model echo, resume, and orphaned `agy`/tmux after teardown. Cross-check the API — a start failure can leave the TUI empty while the session records an error. ## Watch-outs from the code (verify live) - **Placeholder until cold-start.** Before agy mints its real cascade id, bridge state holds an `agy_conv_*` placeholder and RPC is skipped; a turn fired too early just queues into the TUI. - **Permission gating is all-or-nothing + post-hoc.** agy honors only `--dangerously-skip-permissions` (no firing pre-tool hook), so a headless launch auto-bypasses and the genuine Omnigent gate is the elicitation + post-hoc audit (`antigravity_native_audit`), not a per-tool pre-empt.
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub