-
Notifications
You must be signed in to change notification settings - Fork 0
e2b-adk v0.1.0: Google ADK plugin backed by E2B #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
37 commits
Select commit
Hold shift + click to select a range
abd1178
phase(e2b-adk-plugin): Foundation & Scaffolding
tomasvarga cc5ad1e
fix: address Codex review findings (phase e2b-adk-plugin checkpoint)
tomasvarga 63ac12a
fix gitignore after renaming planning folder
max-sudolabs c0ab0cd
ignore .env and add python-dotenv for examples
max-sudolabs b0bff6f
phase(e2b-adk-plugin): Execution tools + plugin
max-sudolabs 0662846
fix: harden run_code and sandbox lifecycle
max-sudolabs c3da715
test: cover SandboxManager concurrency
max-sudolabs ef9da50
phase(e2b-adk-plugin): File & background tools
max-sudolabs 1b1ff91
fix: harden tools against malformed calls and preview-URL failures
max-sudolabs 34d1a19
fix: keep sandbox alive across tool calls and scope plugin callbacks
max-sudolabs 6c273e7
test: add live E2B smoke suite behind a 'live' marker
max-sudolabs e615054
test: assert before_tool_callback returns None for owned tools
max-sudolabs f86ff34
test: cover failure paths in the live smoke suite
max-sudolabs cf1f3b9
add data-analysis example and default examples to gemini-2.5-flash
max-sudolabs 98d0ff1
add developer README
max-sudolabs 954bfa6
add CI workflow and finalize packaging metadata
max-sudolabs 2e82fb9
feat: expose full sandbox create() options as neutral passthroughs
max-sudolabs 098e0de
docs: correct README truncation marker to match code
max-sudolabs 16886cf
ci: install with --locked so CI fails on lockfile drift
max-sudolabs 3ecb833
docs: document the full sandbox passthrough options in README
max-sudolabs e86fb4e
build: allowlist sdist contents so local builds can't leak untracked …
max-sudolabs ba42e1e
fix: read the default sandbox timeout from the public AsyncSandbox class
max-sudolabs 8967d1e
docs: document keep-alive and lifecycle semantics, drop redundant exa…
max-sudolabs 257805e
docs: add the E2B website page for the Google ADK integration
max-sudolabs e6dd8ff
docs: spell out the two-tier result contract and output bound
max-sudolabs 9ff5121
docs: add documentatio page for ADK site
max-sudolabs fefd3c6
refactor: split tools into one module per tool under e2b_adk/tools/
max-sudolabs fdc68f3
docs: correct copyright holder and trim redundant README prose
max-sudolabs 54d49e3
ci: pin action SHAs, add least-privilege token and uv cache
max-sudolabs 18dd04d
chore: stop tracking docs/ (staged locally for separate docs-repo PRs)
max-sudolabs 543cc6f
ci: restore dev-extra install in the build job
max-sudolabs 22c8218
fix: redact command arguments in debug logs
max-sudolabs e23bc76
fix: normalize malformed UTF-8 tool output
max-sudolabs 602b3b8
fix: validate background command preview ports
max-sudolabs 10cff0d
fix: treat background commands as regular ADK tool calls
max-sudolabs 881e9ef
fix: keep sandboxes alive during active operations
max-sudolabs 60c21f7
chore: harden v0.1.0 release
max-sudolabs File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| E2B_API_KEY= | ||
| GOOGLE_API_KEY= |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| name: CI | ||
|
|
||
| on: | ||
| push: | ||
| branches: [main] | ||
| pull_request: | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| lint-type-test: | ||
| runs-on: ubuntu-latest | ||
| strategy: | ||
| fail-fast: false | ||
| matrix: | ||
| python-version: ["3.10", "3.11", "3.12", "3.13"] | ||
| steps: | ||
| - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 | ||
|
|
||
| - name: Install uv | ||
| uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 | ||
| with: | ||
| python-version: ${{ matrix.python-version }} | ||
| enable-cache: true | ||
| cache-dependency-glob: uv.lock | ||
|
|
||
| - name: Install dependencies | ||
| run: uv sync --locked --extra dev | ||
|
|
||
| - name: Lint | ||
| run: uv run ruff check e2b_adk tests examples | ||
|
|
||
| - name: Type check | ||
| run: uv run mypy e2b_adk | ||
|
|
||
| - name: Test | ||
| # Tests mock the E2B sandbox — no credentials or network access needed. | ||
| run: uv run pytest | ||
|
|
||
| build: | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 | ||
|
|
||
| - name: Install uv | ||
| uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 | ||
| with: | ||
| python-version: "3.12" | ||
| enable-cache: true | ||
| cache-dependency-glob: uv.lock | ||
|
|
||
| - name: Install dependencies | ||
| run: uv sync --locked --extra dev | ||
|
|
||
| - name: Build sdist and wheel | ||
| run: uv run python -m build | ||
|
|
||
| - name: Check package metadata | ||
| run: uv run twine check dist/* | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,72 @@ | ||
| name: Release | ||
|
|
||
| on: | ||
| release: | ||
| types: [published] | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| build: | ||
| name: Build and test distributions | ||
| runs-on: ubuntu-latest | ||
|
|
||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 | ||
|
|
||
| - name: Set up uv | ||
| uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 | ||
| with: | ||
| version: "0.11.19" | ||
| python-version: "3.13" | ||
| enable-cache: true | ||
| cache-dependency-glob: uv.lock | ||
|
|
||
| - name: Install dependencies | ||
| run: uv sync --locked --extra dev | ||
|
|
||
| - name: Run unit tests | ||
| run: uv run pytest | ||
|
|
||
| - name: Run lint checks | ||
| run: uv run ruff check e2b_adk tests examples | ||
|
|
||
| - name: Run type checks | ||
| run: uv run mypy e2b_adk | ||
|
|
||
| - name: Build distributions | ||
| run: uv run python -m build | ||
|
|
||
| - name: Check package metadata | ||
| run: uv run twine check dist/* | ||
|
|
||
| - name: Upload distributions | ||
| uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5 | ||
| with: | ||
| name: python-package-distributions | ||
| path: dist/* | ||
| if-no-files-found: error | ||
| retention-days: 7 | ||
|
|
||
| publish: | ||
| name: Publish to PyPI | ||
| needs: build | ||
| runs-on: ubuntu-latest | ||
| environment: | ||
| name: pypi | ||
| url: https://pypi.org/p/e2b-adk | ||
| permissions: | ||
| contents: read | ||
| id-token: write | ||
|
|
||
| steps: | ||
| - name: Download distributions | ||
| uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5 | ||
| with: | ||
| name: python-package-distributions | ||
| path: dist | ||
|
|
||
| - name: Publish distributions to PyPI | ||
| uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # release/v1 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,2 +1,33 @@ | ||
| .claude/ | ||
| e2b-adk/ | ||
| /.agents/ | ||
| /.claude/ | ||
| /.planning/ | ||
|
|
||
| # Python | ||
| .venv/ | ||
| __pycache__/ | ||
| *.pyc | ||
| dist/ | ||
| build/ | ||
| *.egg-info/ | ||
| .pytest_cache/ | ||
| .mypy_cache/ | ||
| .ruff_cache/ | ||
|
|
||
| # Docs staged here for separate PRs to other repos (e2b-dev/docs, google/adk-docs) | ||
| /docs/ | ||
|
|
||
| # Features | ||
| /features/ | ||
| /CLAUDE.md | ||
| /AGENTS.md | ||
| /CLAUDE_ADDITIONS.md | ||
| /AGENTS_ADDITIONS.md | ||
| /DECISIONS.md | ||
|
|
||
| # Environment | ||
| .env | ||
| !.env.example | ||
|
|
||
| # Verification credentials (never commit) | ||
| .env.verification | ||
| /.claude/verification/auth-state.json |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| * @OndrejDrapalik |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| MIT License | ||
|
|
||
| Copyright (c) 2026 FoundryLabs, Inc. | ||
|
|
||
| Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| of this software and associated documentation files (the "Software"), to deal | ||
| in the Software without restriction, including without limitation the rights | ||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| copies of the Software, and to permit persons to whom the Software is | ||
| furnished to do so, subject to the following conditions: | ||
|
|
||
| The above copyright notice and this permission notice shall be included in all | ||
| copies or substantial portions of the Software. | ||
|
|
||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| SOFTWARE. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1,151 @@ | ||
| # e2b-adk-plugin | ||
| # e2b-adk | ||
|
|
||
| A [Google ADK](https://google.github.io/adk-docs/) plugin backed by | ||
| [E2B](https://e2b.dev) sandboxes. Attach it to an ADK agent and the agent can | ||
| generate and run code, execute shell commands, read and write files, and start | ||
| long-running processes — all inside an isolated E2B sandbox, with no | ||
| infrastructure of your own. | ||
|
|
||
| Under the hood every tool call runs in the | ||
| [E2B Code Interpreter](https://e2b.dev/docs), so the model's code executes in a | ||
| real Python kernel instead of being trusted blind. | ||
|
|
||
| ## Install | ||
|
|
||
| ```bash | ||
| pip install e2b-adk | ||
| ``` | ||
|
|
||
| Or with [uv](https://docs.astral.sh/uv/): | ||
|
|
||
| ```bash | ||
| uv pip install e2b-adk | ||
| ``` | ||
|
|
||
| ## Credentials | ||
|
|
||
| You need an E2B API key for the sandbox and a model key for the agent's LLM | ||
| (Gemini is ADK's default): | ||
|
|
||
| ```bash | ||
| export E2B_API_KEY="..." | ||
| export GOOGLE_API_KEY="..." | ||
| ``` | ||
|
|
||
| Get an E2B key at [e2b.dev/dashboard](https://e2b.dev/dashboard). | ||
|
|
||
| ## Quickstart | ||
|
|
||
| Create the plugin, hand its tools to an `Agent`, and register the plugin on the | ||
| `App`. The plugin owns the sandbox lifecycle — you never create or tear one down | ||
| yourself. | ||
|
|
||
| ```python | ||
| import asyncio | ||
|
|
||
| from google.adk.agents import Agent | ||
| from google.adk.apps import App | ||
| from google.adk.runners import InMemoryRunner | ||
|
|
||
| from e2b_adk import E2BPlugin | ||
|
|
||
|
|
||
| async def main() -> None: | ||
| plugin = E2BPlugin() | ||
| agent = Agent( | ||
| model="gemini-2.5-flash", | ||
| name="coder", | ||
| instruction="Write and run Python to answer the user. Verify with run_code.", | ||
| tools=plugin.get_tools(), | ||
| ) | ||
| app = App(name="demo", root_agent=agent, plugins=[plugin]) | ||
|
|
||
| async with InMemoryRunner(app=app) as runner: | ||
| # run_debug prints the conversation as it runs. | ||
| await runner.run_debug("Compute the 20th Fibonacci number.") | ||
|
|
||
|
|
||
| asyncio.run(main()) | ||
| ``` | ||
|
|
||
| ## Tools | ||
|
|
||
| `plugin.get_tools()` returns six tools. Each returns a JSON-serializable dict | ||
| with a `success` flag — tools report failures in the result rather than raising, | ||
| so a bad call never aborts the agent run. `success` means the tool *ran*: code | ||
| that raised or a command that exited non-zero still returns `success: True` with | ||
| the failure captured in `error` / `exit_code`. Only a call that could not run at | ||
| all (sandbox unavailable, timeout) returns `success: False`. | ||
|
|
||
| | Tool | Does | Key result fields | | ||
| |------|------|-------------------| | ||
| | `run_code` | Run code in a stateful kernel (variables persist across calls) | `stdout`, `stderr`, `text`, `error` | | ||
| | `run_command` | Run a shell command (each call is independent) | `stdout`, `stderr`, `exit_code` | | ||
| | `write_file` | Write a file in the sandbox | `path` | | ||
| | `read_file` | Read a file back | `path`, `content` | | ||
| | `list_files` | List a directory | `entries` (`[{name, type}]`) | | ||
| | `start_background_command` | Start a long-running process; optional preview URL for a port | `pid`, `preview_url` (+ `readiness` when `port` is set) | | ||
|
|
||
| ### Configuration | ||
|
|
||
| `E2BPlugin` takes keyword-only options, all optional. Anything you don't set | ||
| falls back to the E2B SDK's own default — the plugin overrides none of them. | ||
|
|
||
| ```python | ||
| E2BPlugin( | ||
| # Common options | ||
| api_key=None, # defaults to the E2B_API_KEY env var | ||
| template=None, # E2B sandbox template | ||
| metadata=None, # dict[str, str] attached to the sandbox | ||
| envs=None, # dict[str, str] environment variables | ||
| timeout=None, # sandbox timeout in seconds (re-applied on every tool call) | ||
| plugin_name="e2b_plugin", | ||
| # Passed straight through to AsyncSandbox.create() — see the E2B SDK docs | ||
| secure=None, | ||
| allow_internet_access=None, | ||
| mcp=None, | ||
| network=None, | ||
| lifecycle=None, # e.g. pause/auto-resume; defaults to E2B's behavior | ||
| volume_mounts=None, | ||
| # **opts: any other AsyncSandbox.create() / connection param (proxy, request_timeout, …) | ||
| ) | ||
| ``` | ||
|
|
||
| ## Examples | ||
|
|
||
| - [`examples/data_analysis.py`](examples/data_analysis.py) — a data-analysis | ||
| agent that saves a dataset and computes real answers with pandas instead of | ||
| guessing. | ||
| - [`examples/code_generator.py`](examples/code_generator.py) — a code generator | ||
| that executes every snippet before returning it. | ||
|
|
||
| Run one with the credentials above: | ||
|
|
||
| ```bash | ||
| uv run --extra examples python examples/data_analysis.py | ||
| ``` | ||
|
|
||
| ## v1 notes | ||
|
|
||
| - **Sequential tool calls.** One sandbox is shared per plugin; calls are meant | ||
| to run one at a time, not concurrently. Concurrent calls are safe for the | ||
| sandbox lifecycle (creation is locked) but share one filesystem and kernel. | ||
| - **Text-only output.** Tools return text — `stdout`/`stderr`/file contents. | ||
| Rich results (charts, dataframes, images) are not surfaced yet. | ||
| - **Lazy, shared sandbox lifecycle.** The sandbox is created on the first tool | ||
| call and reused for the agent's lifetime, then killed when the runner exits. | ||
| - **Keep-alive while active, expiry when idle.** While a sandbox SDK operation | ||
| is active, a best-effort heartbeat refreshes its `timeout` (E2B's default is | ||
| 300s), including when a foreground call runs longer than that timeout. A | ||
| final refresh starts the full idle window when the operation ends. An idle | ||
| gap longer than `timeout` still expires the sandbox under E2B's default | ||
| lifecycle (`on_timeout: kill`), and later tool calls return failure results — | ||
| pass e.g. | ||
| `lifecycle={"on_timeout": {"action": "pause"}, "auto_resume": True}` to pause | ||
| and auto-resume across idle gaps instead. | ||
| - **Bounded output.** Output larger than 10 KB per field is truncated with a | ||
| `…[truncated N bytes]` marker so a single call can't flood the model's context. | ||
|
|
||
| ## License | ||
|
|
||
| MIT — see [LICENSE](LICENSE). |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| """e2b-adk — Google ADK plugin backed by E2B sandboxes.""" | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| from e2b import ConnectionConfig | ||
|
|
||
| from .plugin import E2BPlugin | ||
| from .tools import ( | ||
| ListFiles, | ||
| ReadFile, | ||
| RunCode, | ||
| RunCommand, | ||
| StartBackgroundCommand, | ||
| WriteFile, | ||
| ) | ||
|
|
||
| # E2B's integration hook is process-wide and must be set before the SDK builds | ||
| # a connection config. Importing the modules above has no runtime side effects; | ||
| # plugin construction can only happen after package initialization completes. | ||
| ConnectionConfig.set_integration("e2b-adk/0.1.0") | ||
|
|
||
| __all__ = [ | ||
| "E2BPlugin", | ||
| "ListFiles", | ||
| "ReadFile", | ||
| "RunCode", | ||
| "RunCommand", | ||
| "StartBackgroundCommand", | ||
| "WriteFile", | ||
| ] |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.