Run Google ADK agents in a Promigence sandbox
Point a Google ADK agent's environment tools or code executor at a Promigence sandbox, so its commands and code run there.
Also as markdown: /docs/guides/google-adk.md
Google ADK runs an agent's work in two places: environment tools that execute commands and edit files, and a code executor that runs the code blocks a model writes. The promigence-google-adk package covers both. PromigenceEnvironment backs EnvironmentToolset, and PromigenceCodeExecutor backs LlmAgent(code_executor=...). Either way the work happens in a Promigence sandbox instead of on the machine serving the agent.
What you need#
- A Promigence API key in
PROMIGENCE_API_KEY. - Python 3.10 or newer.
google-adk2.9.1.BaseEnvironmentis marked experimental upstream, so the package pins>=2.9,<3.
Install#
pip install promigence-google-adk
export PROMIGENCE_API_KEY=prom_api_...
export PROMIGENCE_SPEND_CAP=5
Language: bashEnvironment tools#
from google.adk.agents import LlmAgent
from google.adk.tools.environment import EnvironmentToolset
from promigence_google_adk import PromigenceEnvironment
agent = LlmAgent(
name="coder",
model="gemini-2.5-pro",
tools=[EnvironmentToolset(environment=PromigenceEnvironment(image="python:3.12-slim", spend_cap=1))],
)
Language: pythonThe toolset hands the model Execute, ReadFile, EditFile and WriteFile tools, all acting on the sandbox. EnvironmentToolset initializes the environment before it hands out the tools and closes it when the toolset closes, so the sandbox lives exactly as long as the toolset. To manage it by hand, use async with PromigenceEnvironment(...) as env:.
Code executor#
from google.adk.agents import LlmAgent
from promigence_google_adk import PromigenceCodeExecutor
executor = PromigenceCodeExecutor(image="python:3.12-slim", spend_cap=1)
agent = LlmAgent(name="analyst", model="gemini-2.5-pro", code_executor=executor)
# ... run the agent ...
executor.close()
Language: pythonEach code block is written to a file in the sandbox and run with python3 (change it with interpreter). Input files from ADK are written into the working directory first.
Both classes take the environment the same way: pass exactly one of image or snapshot. The first time you name an image, Promigence pins its tag to a digest, builds a snapshot from it and checks it, and later sandboxes reuse that snapshot: a saved, verified environment that each sandbox starts as a fresh copy of. That build is billed per second while it runs, whether it verifies or not, up to $0.17 at the default small size (quickstart), and the sandboxes that reuse the snapshot pay no further build charge. Both also take tier, working_dir, env_vars, allow_internet_access=False and registry_secret for a private image. user and the snapshot inputs setup, verify and start belong to PromigenceEnvironment only; the code executor ignores them. Only the code executor takes interpreter.
How sandboxes map to ADK#
An environment is one sandbox. The code executor is stateful, with one sandbox per ADK session: a session's blocks see each other's files and installed packages, and two sessions never share one. One app process can serve many users without one user's code seeing another's files. The executor keeps the sandbox id in session state, so a restarted process reconnects to the running sandbox instead of starting another.
Setting a spend cap#
spend_cap is the most one sandbox may spend, in US dollars. It falls back to PROMIGENCE_SPEND_CAP, then to your organization's default; with none of them, the adapter raises a configuration error before anything starts. sandbox_timeout_sec (3600 by default) bounds each sandbox's lifetime.
Cleaning up#
The toolset closes its environment for you. The code executor has no shutdown hook in ADK, so call executor.close() yourself or use it as a context manager. It ends every session's sandbox. Each one also ends on its own when sandbox_timeout_sec runs out.
A command that exits non-zero comes back as an ExecutionResult with its exit code, the way ADK expects. A failure on the Promigence side raises PromigenceAdapterError with failure_class == "env", so it never looks like the agent's command failed.
What is not supported yet#
- Streaming output. A command's output arrives when it finishes.
output_filesfrom the code executor. It is always empty; read generated files withread_file.- The code executor cannot tell a timeout from a kill.
PromigenceEnvironment.executecan. - The image needs
/bin/sh, plus the interpreter you name for the code executor.
Next#
Read the agent briefing for what a sandbox gives an agent, or start with the quickstart.