Skip to content

Commit 4cb27fd

Browse files
Jacksunweicopybara-github
authored andcommitted
feat(labs): add experimental Antigravity SDK agent wrapper
Merge #6040 ## Summary Introduces `google.adk.labs.antigravity` with `AntigravityAgent`, a `BaseAgent` that runs a Google Antigravity SDK agent (described by an `AgentConfig`) as a native ADK root agent. Lives under `labs/` as an experimental feature. - Delegates each turn to a fresh SDK `Agent` and maps trajectory steps to ADK events: model text, function calls/responses, and SSE-gated partial thinking/text deltas. - Folder-based session resumption via `config.save_dir`: renames the harness trajectory to a deterministic per-session name and skips replayed steps using a persisted resume step index. - Restricted to standalone root use (local mode only) with construction-time guards (cannot be a sub-agent or have sub-agents). - `serialize_agent` now honors `Field(exclude=True)` so the non-serializable `config` does not break the `build_graph` endpoint. - Adds the `[antigravity]` optional extra (in both the extra and `test` groups, with `uv.lock` updated), a game-developer sample, package README, and unit tests. ## Test plan - [ ] `pytest tests/unittests/labs/antigravity/` (22 unit tests pass) - [ ] `pytest tests/unittests/cli/utils/test_graph_serialization.py` - [ ] Manual: run the sample via `adk web` with `GEMINI_API_KEY` and a `[antigravity]` install, confirm multi-turn resumption and no duplicate events. Co-authored-by: Wei Sun (Jack) <[email protected]> COPYBARA_INTEGRATE_REVIEW=#6040 from google:feat/antigravity-agent 304f6ad PiperOrigin-RevId: 930712984
1 parent 68927c2 commit 4cb27fd

14 files changed

Lines changed: 1260 additions & 3 deletions

File tree

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Workspace the agent writes generated games into at runtime.
2+
game_repo/
3+
# Conversation trajectories persisted across turns.
4+
trajectories/
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
# Antigravity SDK Game Developer Agent
2+
3+
## Overview
4+
5+
This sample wraps a pre-configured [Google Antigravity SDK](https://pypi.org/project/google-antigravity/)
6+
agent as a native ADK agent using `AntigravityAgent`, configured as a
7+
**game developer** that writes small, runnable browser games into the
8+
`game_repo/` workspace as single self-contained HTML files. Each turn is
9+
delegated to the Antigravity
10+
runner, and its trajectory steps (model text, tool calls, and tool responses)
11+
are streamed back as standard ADK events recorded in the session.
12+
13+
`AntigravityAgent` must be used as a **standalone root agent** (the SDK currently
14+
only supports local mode). See the
15+
[package README](../../../../src/google/adk/labs/antigravity/README.md)
16+
for the full setup, limitations, and API details.
17+
18+
## Prerequisites
19+
20+
- Install the SDK: `pip install "google-adk[antigravity]"`
21+
- Set a Gemini API key: `export GEMINI_API_KEY="your-api-key"`
22+
(required by the Antigravity SDK, which drives the model)
23+
24+
The agent writes generated games into a `game_repo/` directory and persists
25+
conversation trajectories (for cross-turn resumption) into a `trajectories/`
26+
directory, both next to `agent.py` and created automatically on import.
27+
28+
## Sample Inputs
29+
30+
- `Create a playable Snake game.`
31+
32+
The agent writes a self-contained HTML implementation into `game_repo/` (e.g.
33+
`game_repo/snake.html`, with inline CSS and JavaScript) using the built-in
34+
`create_file` tool, then explains how to open it in a browser.
35+
36+
- `Create a 2-player turn-based Artillery game with adjustable angle and power.`
37+
38+
The agent writes another self-contained HTML game (e.g.
39+
`game_repo/artillery.html`) with canvas rendering and projectile physics.
40+
41+
- `Create a Brick Breaker game.`
42+
43+
The agent writes a self-contained HTML implementation (e.g.
44+
`game_repo/brick_breaker.html`) with a paddle, ball, and breakable bricks.
45+
46+
## Graph
47+
48+
Each turn, the wrapper delegates to the SDK agent's local Go harness and maps
49+
the trajectory steps it streams back into ADK events:
50+
51+
```mermaid
52+
graph LR
53+
Runner[ADK Runner] -->|prompt| Wrapper[AntigravityAgent]
54+
Wrapper -->|send| SDK[Antigravity SDK Agent]
55+
SDK -->|local mode| Harness[Go localharness]
56+
Harness -->|steps| SDK
57+
SDK -->|steps| Wrapper
58+
Wrapper -->|ADK events| Runner
59+
```
60+
61+
## How To
62+
63+
The wrapper takes a `google.antigravity.LocalAgentConfig` via the `config`
64+
argument:
65+
66+
```python
67+
root_agent = AntigravityAgent(
68+
name="antigravity_game_developer",
69+
description="...",
70+
config=_sdk_config,
71+
)
72+
```
73+
74+
The SDK agent enables its built-in file tools by default; the
75+
`policy.workspace_only([...])` policy keeps all file reads and writes contained
76+
to `game_repo/`. Internally, `AntigravityAgent._run_async_impl` deep-copies the
77+
config per turn (the SDK's `AsyncExitStack` is single-use), enters a fresh SDK
78+
`Agent`, sends the latest user prompt, and converts each streamed Step into ADK
79+
events.
80+
81+
The root-only restriction is enforced at construction time: giving the agent
82+
`sub_agents`, or adopting it under a parent agent, raises a `ValueError`.
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# Copyright 2026 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""Game-developer agent that writes browser games as self-contained HTML.
16+
17+
Wraps a Google Antigravity SDK agent as an ADK agent. See the package README
18+
for setup and details.
19+
"""
20+
21+
import os
22+
23+
from google.adk.labs.antigravity import AntigravityAgent
24+
from google.antigravity import LocalAgentConfig
25+
from google.antigravity.hooks import policy
26+
27+
# 1. Configure the Google Antigravity SDK game-developer agent. The
28+
# workspace-scoped policy lets it create and edit files inside the game_repo
29+
# workspace (built-in file tools are allowed there) while keeping writes
30+
# contained.
31+
_sample_dir = os.path.dirname(os.path.abspath(__file__))
32+
_workspace = os.path.join(_sample_dir, "game_repo")
33+
_trajectories = os.path.join(_sample_dir, "trajectories")
34+
os.makedirs(_workspace, exist_ok=True)
35+
os.makedirs(_trajectories, exist_ok=True)
36+
_sdk_config = LocalAgentConfig(
37+
system_instructions="""\
38+
You are a senior web game developer. You build small, runnable games on request \
39+
as a single self-contained HTML file with inline CSS and JavaScript (no external \
40+
assets or third-party dependencies). Write the HTML file into the allowed \
41+
workspace using a clean absolute filesystem path.
42+
43+
Build the file incrementally: first create it with a minimal skeleton (HTML \
44+
structure, canvas, and empty script), then add CSS and the game logic over a \
45+
few substantial edits. Group each edit around a complete feature (e.g. all \
46+
styling, then rendering, then input handling) rather than many tiny changes, \
47+
but do not attempt to write the entire game in one step.
48+
49+
After the file is complete, briefly explain how to play it (open the .html file \
50+
in a browser).""",
51+
workspaces=[_workspace],
52+
policies=[*policy.workspace_only([_workspace])],
53+
save_dir=_trajectories,
54+
)
55+
56+
# 2. Wrap the SDK config as a standalone ADK root agent.
57+
root_agent = AntigravityAgent(
58+
name="antigravity_game_developer",
59+
description=(
60+
"Builds small, runnable games inside the game_repo workspace via the"
61+
" Antigravity SDK."
62+
),
63+
config=_sdk_config,
64+
)

pyproject.toml

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@ dependencies = [
4848
"pydantic>=2.12,<3",
4949
"python-dotenv>=1,<2",
5050
"python-multipart>=0.0.9,<1",
51-
# go/keep-sorted start
5251
"pyyaml>=6.0.2,<7",
5352
"requests>=2.32.4,<3",
5453
"starlette>=1.0.1,<2",
@@ -58,7 +57,6 @@ dependencies = [
5857
"uvicorn>=0.34,<1",
5958
"watchdog>=6,<7",
6059
"websockets>=15.0.1,<16",
61-
# go/keep-sorted end
6260
]
6361

6462
optional-dependencies.a2a = [
@@ -97,9 +95,15 @@ optional-dependencies.all = [
9795
"sqlalchemy-spanner>=1.14",
9896
]
9997

98+
optional-dependencies.antigravity = [
99+
"google-antigravity>=0.1,<0.2",
100+
"protobuf>=6",
101+
]
102+
100103
optional-dependencies.community = [
101104
"google-adk-community",
102105
]
106+
103107
optional-dependencies.db = [
104108
"sqlalchemy>=2,<3",
105109
"sqlalchemy-spanner>=1.14",
@@ -195,6 +199,7 @@ optional-dependencies.test = [
195199
"crewai[tools]; python_version>='3.11' and python_version<'3.12'", # For CrewaiTool tests; chromadb/pypika fail on 3.12+
196200
"e2b>=2,<3",
197201
"gepa>=0.1",
202+
"google-antigravity>=0.1,<0.2",
198203
"google-api-python-client>=2.157,<3",
199204
"google-cloud-agentidentitycredentials>=0.1,<0.2",
200205
"google-cloud-aiplatform[agent-engines,evaluation]>=1.148.1,<2",
@@ -227,6 +232,7 @@ optional-dependencies.test = [
227232
"opentelemetry-instrumentation-google-genai>=0.3b0,<1",
228233
"opentelemetry-resourcedetector-gcp>=1.9.0a0,<2",
229234
"pandas>=2.2.3",
235+
"protobuf>=6",
230236
"pyarrow>=14",
231237
"pypika>=0.50",
232238
"pytest>=9,<10",

src/google/adk/cli/utils/graph_serialization.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ def serialize_agent(agent: BaseAgent) -> dict[str, Any]:
110110
agent_dict = {}
111111

112112
for field_name, field_info in agent.__class__.model_fields.items():
113-
if field_name in SKIP_FIELDS:
113+
if field_name in SKIP_FIELDS or (field_info and field_info.exclude):
114114
continue
115115

116116
value = getattr(agent, field_name, None)
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
# Antigravity SDK Integration
2+
3+
The ADK Antigravity integration provides `AntigravityAgent`, which runs a
4+
[Google Antigravity SDK](https://pypi.org/project/google-antigravity/) agent —
5+
described by an `AgentConfig` — as a native ADK `BaseAgent`. Each turn is
6+
delegated to the Antigravity runner, and its trajectory steps (model text, tool
7+
calls, and tool responses) are streamed back as standard ADK events recorded in
8+
the session.
9+
10+
## Prerequisites
11+
12+
Install the ADK with Antigravity support:
13+
14+
```bash
15+
pip install "google-adk[antigravity]"
16+
```
17+
18+
Set a Gemini API key (used by the SDK agent):
19+
20+
```bash
21+
export GEMINI_API_KEY="your-api-key"
22+
```
23+
24+
Set `save_dir` on the config — it is the folder where conversation trajectories
25+
are persisted so sessions resume across turns (see
26+
[Session Resumption](#session-resumption)).
27+
28+
## Limitations
29+
30+
The Antigravity SDK currently only supports its **local mode** (an in-process
31+
Go harness that owns its own session lifecycle). Because of this, an
32+
`AntigravityAgent` must be used as a **standalone root agent**:
33+
34+
- It cannot be given `sub_agents`.
35+
- It cannot be nested under a parent agent.
36+
37+
Both are rejected at construction time. This restriction is temporary and will
38+
be lifted once the SDK supports remote connection modes.
39+
40+
## Usage
41+
42+
```python
43+
from google.adk.labs.antigravity import AntigravityAgent
44+
from google.antigravity import LocalAgentConfig
45+
from google.antigravity.hooks import policy
46+
47+
# 1. Configure the Antigravity SDK agent. ``save_dir`` is the folder where
48+
# conversation trajectories are persisted for resumption.
49+
sdk_config = LocalAgentConfig(
50+
system_instructions="You are a helpful local environment assistant.",
51+
workspaces=["./sandbox"],
52+
policies=[*policy.workspace_only(["./sandbox"])],
53+
save_dir="./trajectories",
54+
)
55+
56+
# 2. Wrap the config as a standalone ADK root agent.
57+
root_agent = AntigravityAgent(
58+
name="antigravity_assistant",
59+
description="Runs an Antigravity SDK agent inside ADK.",
60+
config=sdk_config,
61+
)
62+
```
63+
64+
For a runnable end-to-end example, see
65+
`contributing/samples/integrations/antigravity_agent/`.
66+
67+
## How It Works
68+
69+
`AntigravityAgent._run_async_impl` deep-copies `config` on every turn (the SDK
70+
`Agent`'s `AsyncExitStack` is single-use, so a fresh instance is needed for each
71+
of the stateless turns of a long-lived server), enters a fresh SDK `Agent`, sends
72+
the latest user prompt, and converts each streamed Step into ADK events.
73+
74+
Step-to-event mapping covers model text responses, function calls, and function
75+
responses. In SSE streaming mode (`RunConfig(streaming_mode=StreamingMode.SSE)`),
76+
incremental thinking and text deltas are additionally emitted as `partial=True`
77+
events as they arrive, followed by the final aggregated response event — matching
78+
ADK's standard streaming behavior. In the default non-streaming mode, only final
79+
events are emitted.
80+
81+
## Session Resumption
82+
83+
The SDK's local harness persists conversation state to a `traj-*` file in
84+
`config.save_dir` and rehydrates it when a matching `conversation_id` is passed
85+
on a later turn. The wrapper keys this on the ADK session:
86+
87+
- **Fresh turn**: no `conversation_id` is passed, so the harness writes a
88+
randomly-named `traj-<random>` file. After the turn, the wrapper renames it to
89+
`traj-<session_id>_<agent_name>` so later turns can find it.
90+
- **Resume turn**: when `traj-<session_id>_<agent_name>` already exists, the
91+
wrapper passes that `conversation_id` so the harness rehydrates the
92+
conversation.
93+
94+
On resume, the harness replays the entire rehydrated trajectory through its step
95+
stream before producing new steps. To avoid re-emitting prior turns into the ADK
96+
session, the **resume step index** (the highest harness `step_index` already
97+
emitted) is persisted in a `traj-<...>.resume` file alongside the trajectory;
98+
steps at or below it are skipped.
99+
100+
`config.save_dir` is required, and because the trajectory lives on disk there,
101+
conversations survive server restarts as long as the folder persists.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Copyright 2026 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
try:
16+
import google.antigravity # noqa: F401
17+
except ImportError as e:
18+
raise ImportError(
19+
"The 'google-antigravity' package is required to use the ADK"
20+
' Antigravity integration. Install it with: pip install'
21+
' "google-adk[antigravity]"'
22+
) from e
23+
24+
from ._antigravity_agent import AntigravityAgent
25+
26+
__all__ = [
27+
'AntigravityAgent',
28+
]

0 commit comments

Comments
 (0)