Architecture Deep Dive¶
Antimatter bridges the gap between your local development environment and your mobile device without requiring any official APIs from the host IDE. Instead, it reverse-engineers the AntiGravity agent's file-system output and exposes it over a secure WebSocket channel.
TL;DR
AntiGravity writes agent logs to disk → the extension watches them with fs.watch → parses into TrajectoryStep objects → streams over a WebSocket → through a Cloudflare tunnel → to the Android app, which renders them in real-time with Jetpack Compose.
High-Level Architecture¶
Antimatter operates through two distinct bridges depending on your environment, which both route into the exact same secure Cloudflare tunnel and Android client:
┌──────────────────────┐ ┌──────────────────────────────────┐
│ AntiGravity IDE │ │ VS Code Extension (Bridge) │
│ │ │ ├─ BrainWatcher (fs.watch) │
│ Agent writes logs │ ─────▶ │ ├─ BridgeWebSocketServer (ws) │
│ to transcript.jsonl │ │ └─ Terminal proxy (node-pty) │
└──────────────────────┘ │ │
│ │
┌──────────────────────┐ │ Antigravity 2.0 (Daemon) │
│ Antigravity 2.0 │ │ ├─ agent_bridge.py │
│ │ │ ├─ asyncio WebSocket Server │
│ Core Agent Process │ ─────▶ │ └─ Native Plugin Integration │
└──────────────────────┘ └───────────────┬──────────────────┘
│ Cloudflare Tunnel
▼
┌──────────────────────────────────┐
│ Android App (Client) │
│ │
│ BridgeWebSocket (OkHttp) │
│ ├─ Token auth + Ed25519 verify │
│ └─ emits Flow<InboundMessage> │
│ │
│ Feature Screens (Compose) │
│ ├─ ChatScreen + ChatViewModel │
│ ├─ FilesScreen + FilesViewModel │
│ ├─ TerminalScreen + TerminalVM │
│ └─ ConnectScreen + ConnectionVM │
│ │
│ Room DB (offline history) │
└──────────────────────────────────┘
The File System Monitor (Reverse-Engineering)¶
Since AntiGravity IDE does not provide an official API to stream agent thoughts, Antimatter's IDE Extension uses pure file-system monitoring to intercept the agent's brain activity. (Note: Antigravity 2.0 uses a native SDK plugin and bypasses this reverse-engineering).
When an IDE agent runs, it creates a unique conversation folder:
For the IDE, the extension's BrainWatcher uses fs.watch to monitor the brain/ directory. For Antigravity 2.0, the agent_bridge.py daemon tails the same file directly using asyncio. In both cases, the bridge:
- Detects the most recently modified conversation directory.
- Tails the
transcript.jsonlfile line-by-line. - Parses each JSON line into a
TrajectoryStepobject. - Broadcasts new steps to all connected clients via
STEPframes.
Step types
The agent's trajectory includes many step types: userInput, plannerResponse, text, toolCall, runCommand, approvalInteraction, elicitation, and more. See the full StepCase enum for the complete mapping.
The WebSocket Bridge¶
The bridge runs a WebSocket server bound to 127.0.0.1 (never exposed directly). The IDE extension uses ws, while the Antigravity 2.0 daemon uses Python's websockets library. To bypass firewalls and NATs, the bridge spawns a cloudflared process that creates a secure Cloudflare tunnel.
Connection flow:
- Client connects to the public
wss://URL. - Origin validation — only
vscode-webview://and*.cloudflareaccess.comorigins are accepted (prevents CSWSH). - Token verification — the 256-bit pairing token is checked with
crypto.timingSafeEqual. - Ed25519 handshake — the client sends
AUTH_CHALLENGEwith a nonce; the bridge signs it and returnsAUTH_RESPONSE. - Client is marked authenticated and receives
SESSION_STATE.
Full protocol
See the WebSocket Protocol Reference for every message type, field, and close code.
Message Routing¶
The MessageRouter dispatches each inbound JSON message to the appropriate handler based on its type field:
| Message type | Handler module | Action |
|---|---|---|
AUTH_CHALLENGE |
AuthHandler |
Sign nonce, reply AUTH_RESPONSE |
SEND_MESSAGE, NEW_CONVERSATION, CANCEL_RESPONSE |
ChatCommandHandler |
Inject into agent via vscode.commands |
ACCEPT_EDITS, REJECT_EDITS, *_HUNK |
ChatCommandHandler |
Proxy diff decisions |
GET_FILES, READ_FILE, WRITE_FILE |
FileCommandHandler |
Serve workspace tree/files |
EXECUTE_COMMAND |
TerminalCommandHandler |
Spawn shell, stream output |
SUBSCRIBE_CONVERSATION, GET_HISTORY, GET_ARTIFACTS |
extension.ts |
Replay trajectory / list conversations |
PING |
extension.ts |
Reply PONG |
If a handler throws, the bridge sends an ERROR frame. Unknown types are logged and ignored.
Remote Terminal Execution¶
The TerminalCommandHandler proxies raw shell commands from the phone to the host:
- Listens for
EXECUTE_COMMANDmessages over the WebSocket. - Uses Node.js
child_process.spawnto launch the host shell. - Streams
stdoutandstderrback asCOMMAND_OUTPUTmessages in real-time.
Security
The terminal runs with the same permissions as the VS Code process. On the client side, this is gated behind a biometric lock (Android androidx.biometric API) so only the physical device owner can execute commands.
Payload Serialization & Compression¶
- Parsed
TrajectoryStepobjects are serialized to JSON and broadcast to connected clients. - Full conversation histories can exceed 10+ MB, so the extension batches them into
STEP_BATCHarrays when a client subscribes. permessage-deflatecompression is enabled on the WebSocket server to reduce bandwidth (configurable chunk size and memory level).- Maximum WebSocket payload: 10 MiB.
The Android Client¶
The app is built natively with Kotlin and Jetpack Compose, following a multi-module MVVM architecture:
| Layer | Technology | Purpose |
|---|---|---|
| Networking | OkHttp + BridgeWebSocket |
WebSocket client, token + Ed25519 auth |
| Background | BridgeService (Foreground Service) |
Keeps socket alive when app is backgrounded |
| Persistence | Room + DataStore | Offline conversation/step/artifact history |
| DI | Hilt | Dependency injection |
| UI | Jetpack Compose | Declarative UI with Material 3 theming |
| Rendering | Custom MarkdownText |
Renders AI Markdown responses with syntax highlighting |
Key rendering decisions:
- Chat uses
reverseLayoutLazyColumnto anchor at the bottom — as AI "thinking" bubbles expand, older messages scroll up smoothly without jitter. - Each
TrajectoryStepmaps to aStepCaseenum that drives rendering: plain text, tool call cards, run-command blocks, approval prompts, etc.
Extensibility¶
Because the core data structure is unified around TrajectoryStep, any future tools or plugins added to AntiGravity will automatically be parsed by the file system monitor and streamed to the mobile app — as long as they write to the JSONL log format.
The modular architecture also makes it straightforward to add new feature modules on both sides:
- Extension: add a new
feature/handler and register it on theMessageRouter. - Android: add a new
:feature:*module with a Screen + ViewModel.
Next Steps¶
- WebSocket Protocol Reference — the full message contract
- VS Code Extension Reference — module map and source layout
- Android App Reference — Gradle module graph and screen/ViewModel map