adk-code
Language-aware code execution toolchain for ADK-Rust.
Overview
adk-code handles compilation, diagnostics, and language-specific pipelines. It delegates actual execution to adk-sandbox backends, cleanly separating language toolchains from isolation.
The crate provides:
- Typed executor abstraction (
CodeExecutortrait) with lifecycle management - Sandbox policy model (
SandboxPolicy,BackendCapabilities) with fail-closed validation - Rust-first code execution via
RustExecutor(check → build → delegate) and legacyRustSandboxExecutor - Embedded JavaScript execution via
EmbeddedJsExecutor(boa_engine,embedded-jsfeature) - Embedded Python execution via
MontyOneShotExecutor/MontyReplExecutor(Pydantic Monty,embedded-pythonfeature) - WASM guest module execution via
WasmGuestExecutor(phase 1 placeholder) - Docker container execution via
DockerExecutor(persistent,dockerfeature) andContainerCommandExecutor(ephemeral, always available) - Vertex AI Agent Engine managed sandboxes via
VertexSandboxClient/SandboxCodeExecutor(vertex-sandboxfeature) CodeToolimplementingadk_core::Toolfor LLM agent integration- Structured Rust compiler diagnostics parsing
- Workspace abstraction for multi-agent collaborative project builds
- A2A-compatible collaboration transport layer
Architecture
Agent → CodeTool (adk-code)
│
▼
RustExecutor
check → build → delegate
│
▼
SandboxBackend (adk-sandbox)
ProcessBackend / WasmBackend
The RustExecutor pipeline:
- Check —
rustc --error-format=json→ parse structured diagnostics → halt on errors - Build — compile to binary using the harness template
- Execute — delegate to a
SandboxBackendviaExecRequest
Quick Start
use ;
use ProcessBackend;
use Arc;
let backend = new;
let executor = new;
let tool = new;
// Use with any LLM agent
let agent = new
.instruction
.tool
.build?;
User code must provide fn run(input: serde_json::Value) -> serde_json::Value. The harness wraps it with fn main(), stdin parsing, and stdout serialization.
Feature Flags
| Feature | Description | Default |
|---|---|---|
| (none) | Core types, RustExecutor, RustSandboxExecutor, ContainerCommandExecutor, WasmGuestExecutor, CodeTool, Workspace |
✅ |
embedded-js |
EmbeddedJsExecutor via boa_engine |
❌ |
embedded-python |
MontyOneShotExecutor / MontyReplExecutor via the Monty interpreter |
❌ |
docker |
DockerExecutor via bollard (persistent Docker containers) |
❌ |
vertex-sandbox |
VertexSandboxClient / SandboxCodeExecutor / VertexSandboxTool — Vertex AI Agent Engine managed sandboxes |
❌ |
Execution Backends
Backend Matrix
| Backend | Isolation | Timeout | Network | Filesystem | Environment | Persistent |
|---|---|---|---|---|---|---|
RustSandboxExecutor |
HostLocal | ✅ | ❌ | ❌ | ✅ | ❌ |
RustExecutor |
Delegated | ✅ | Delegated | Delegated | Delegated | ❌ |
EmbeddedJsExecutor |
InProcess | ✅† | ✅* | ✅* | ✅* | ❌ |
MontyOneShotExecutor |
InProcess | ✅ | ✅* | ✅ | ✅ | ❌ |
MontyReplExecutor |
InProcess | ✅ | ✅* | ✅ | ✅ | ✅ |
WasmGuestExecutor |
InProcess | ✅ | ✅* | ✅* | ✅* | ❌ |
ContainerCommandExecutor |
ContainerEphemeral | ✅ | ✅ | ✅ | ✅ | ❌ |
DockerExecutor |
ContainerPersistent | ✅ | ✅ | ✅ | ✅ | ✅ |
*Enforcement by omission — the engine has no APIs for these operations.
†The deadline is enforced on the result; runtime limits stop the interpreter. See EmbeddedJsExecutor.
validate_policy() rejects every request on a backend that reports enforce_environment_policy: false: both EnvironmentPolicy::None and EnvironmentPolicy::AllowList restrict what the code sees, and such a backend passes the host environment through.
RustSandboxExecutor (legacy)
Host-local Rust compilation and execution. Compiles with rustc, runs the binary as a child process. Enforces timeouts (including stdin delivery), output truncation, and environment restrictions, but not network or filesystem restrictions.
| Process | Environment |
|---|---|
rustc |
Cleared, plus the toolchain allowlist from ProcessBackend::toolchain_env() (PATH, HOME, RUSTUP_HOME, CARGO_HOME, RUSTUP_TOOLCHAIN, TMPDIR, and on Windows LIB, SystemRoot, and similar) |
| Compiled binary | Cleared, plus the variables named by EnvironmentPolicy::AllowList (and SystemRoot on Windows) |
env! and option_env! in user code therefore cannot embed host credentials into the binary, and std::env::var sees only allowlisted variables.
use ;
let executor = new;
RustExecutor (new)
Separates compilation from isolation by delegating execution to a SandboxBackend. The check → build → execute pipeline provides structured diagnostics.
use ;
use ProcessBackend;
use Arc;
let backend = new;
let executor = new;
| Step | Where it runs | Environment |
|---|---|---|
| Check, build | Inside the backend when it reports filesystem_read_isolation and supports Language::Command (Unix only); otherwise on the host |
Cleared, plus the toolchain allowlist |
| Execute | Inside the backend | Whatever the backend exposes; ProcessBackend clears it |
Compile-time file access such as include_str! follows wherever rustc runs. rustc runs on the host only when the backend does not confine reads, so it reaches no file the binary could not also read at run time. On Windows, rustc always runs on the host.
RustExecutor::execute() returns a CodeResult with:
exec_result— sandbox execution result (stdout, stderr, exit_code, duration)diagnostics— compiler warnings from the check stepoutput— structured JSON extracted from the last stdout linedisplay_stdout— everything before the structured output line
EmbeddedJsExecutor (embedded-js feature)
In-process JavaScript execution via boa_engine. Useful for lightweight transforms and deterministic state shaping. No network/filesystem/environment APIs available (enforcement by omission).
use ;
let executor = new;
let request = ExecutionRequest ;
User code is wrapped in an IIFE so return works. Input is injected as a global input variable. Return value is converted to JSON.
Each evaluation runs on its own thread, and execute() returns ExecutionStatus::Timeout once the policy timeout passes. Boa cannot be preempted, so an abandoned evaluation runs until it finishes or reaches a runtime limit:
| Limit | Value |
|---|---|
| Loop iterations per call frame | 10,000,000 |
| Call depth | 512 |
| VM stack values | 10,240 |
Runtime-limit errors cannot be caught by try/catch; a limit reached before the deadline yields ExecutionStatus::Failed. Loop limits count per call frame, work inside a single native builtin (such as a pathological regular expression) is not counted, and there is no heap limit.
Monty executors (embedded-python feature)
In-process Python execution via the Pydantic Monty interpreter — no container, no subprocess, microsecond startup. One builder produces two products: build_one_shot() runs each call in a fresh interpreter, build_repl() persists interpreter state (variables, functions, imports) across calls.
Every OS call Monty can emit (filesystem, os.getenv/os.environ, datetime.now()/date.today()) is serviced against grants the host authors at construction; ungranted access raises a catchable in-script OSError. Monty has no network or subprocess surface at all. Registered host functions become callable Python functions, and both executors describe their built environment through CodeExecutor::prompt_snippet().
use ;
use json;
let builder = new
.allow_path
.allow_path
.environ_var
.system_clock
.function_fn;
let one_shot = builder.clone.build_one_shot?; // fresh interpreter per call
let repl = builder.build_repl?; // state persists across calls
The per-request SandboxPolicy may only narrow within the builder's grants; a request exceeding them is rejected fail-closed before any code runs. The value of the script's final expression becomes ExecutionResult::output; print() output is captured as stdout.
The embedded_python module is also the workspace's shared Monty integration kernel: it exposes the JSON↔Monty conversion (json_to_monty / monty_to_json), the OS-call servicing function (resolve_os_call), PathAccess, and re-exports of the monty / monty-types / monty-fs crates — so the Monty release is pinned exactly once, here. adk-codeact-monty builds its CodeRuntime on this kernel.
WasmGuestExecutor
Executes precompiled .wasm guest modules. Phase 1 is a placeholder that validates module format (magic number, minimum size) but does not execute. Full runtime integration is deferred.
use ;
let executor = with_config;
Accepts only ExecutionPayload::GuestModule with GuestModuleFormat::Wasm. Source payloads are rejected with a descriptive error pointing to EmbeddedJsExecutor or ContainerCommandExecutor.
ContainerCommandExecutor (always available)
Shells out to docker run (or podman) for each execution. Each call spawns a new ephemeral container. Simpler but less efficient than DockerExecutor.
use ;
let executor = new;
Supports Python, JavaScript, and Command languages. Enforces network policy via --network=none, filesystem via bind mounts, and environment via --env.
DockerExecutor (docker feature)
Persistent Docker container that survives across multiple execute() calls. Uses bollard to manage the container lifecycle via the Docker API.
use ;
let executor = new?;
executor.start.await?;
// Multiple executions reuse the same container
let result1 = executor.execute.await?;
let result2 = executor.execute.await?;
executor.cleanup.await?; // Prefer explicit cleanup over Drop
Presets: DockerConfig::python(), DockerConfig::node(), DockerConfig::custom("image").
Builder methods: setup_command(), pip_install(), npm_install(), with_network(), bind_mount(), env().
Lifecycle: start() → execute() (reusable) → stop() / cleanup(). Set auto_start: true (default) to start on first execute.
The container's network mode, bind mounts, and environment are fixed at creation, so execute() checks each request's SandboxPolicy against the DockerConfig with DockerConfig::validate_sandbox_policy() and rejects a stricter request with ExecutionError::UnsupportedPolicy:
| Policy | Satisfied when |
|---|---|
NetworkPolicy::Disabled |
The config does not call with_network() |
FilesystemPolicy::None |
There are no bind mounts |
FilesystemPolicy::WorkspaceReadOnly { root } |
Every bind mount is :ro and its host path lies under root |
FilesystemPolicy::WorkspaceReadWrite { root } |
Every bind mount's host path lies under root |
FilesystemPolicy::Paths { read_only, read_write } |
Every bind mount lies under a read_write path, or is :ro and lies under a read_only path |
EnvironmentPolicy::None |
The config sets no env() variables |
EnvironmentPolicy::AllowList(names) |
Every env() variable is in names; the named host variables are also passed to the exec |
The networked container in the example above therefore needs requests built with NetworkPolicy::Enabled; SandboxPolicy::strict_rust() disables the network and is rejected.
Vertex AI Agent Engine sandboxes (vertex-sandbox feature)
A client for the Agent Engine sandboxEnvironments surface (v1beta1) — fully
managed, isolated code-execution sandboxes under a reasoning engine. Built on
the shared adk-gcp plumbing (ADC credential caching, bounded transport, LRO
polling, scope validation).
use ;
use Arc;
let client = new;
// Direct: create, execute, delete.
let sandbox = client.create_sandbox.await?;
let name = sandbox.name.unwrap;
let result = client.execute_code.await?;
println!;
client.delete_sandbox.await?;
// Managed: per-session lazy creation with recreate-on-not-running semantics
// (adk-python AgentEngineSandboxCodeExecutor parity), plus an agent tool.
let executor = new;
let tool = new;
create_sandbox/delete_sandboxwait their long-running operations;get_sandbox/list_sandboxesare plain reads;:executeis synchronous.execute_codeimplements the chunk conventions shared with adk-python and the Vertex AI SDK: a JSON code chunk,file_name-attributed file chunks, andmsg_out/msg_errconsole output.- Files are limited to 100 MB per request (rejected before sending) and per
response. Every
:executecall resets the sandbox TTL server-side.
CodeTool
CodeTool implements adk_core::Tool (name: code_exec) and dispatches to RustExecutor. Errors are returned as structured JSON, never as ToolError.
Parameters schema:
language—"rust"(default, only supported value in phase 1)code— Rust source code (required)input— optional JSON input passed torun()timeout_secs— 1–300, default 30
Required scopes: code:execute, code:execute:rust
Structured Diagnostics
Compile errors include parsed RustDiagnostic structs with level, message, spans, and error codes:
Use parse_diagnostics(stderr) to parse rustc --error-format=json output into Vec<RustDiagnostic>.
Sandbox Policy Model
SandboxPolicy describes requested execution constraints. BackendCapabilities describes what a backend can enforce. validate_policy() and validate_request() implement fail-closed semantics — execution is rejected before user code runs if the backend cannot enforce a requested control.
Preset policies:
SandboxPolicy::strict_rust()— no network, no filesystem, no env, 30s timeout, 1 MB limitsSandboxPolicy::host_local()— network allowed (host-local can't restrict), 30s timeoutSandboxPolicy::strict_js()— same as strict_rust but 5s timeout
Harness
The harness template (HARNESS_TEMPLATE) wraps user code:
- Injects
use serde_json::Value; - Provides
fn main()that reads JSON from stdin, callsrun(), prints JSON to stdout - Only
serde_jsonis available as an external crate; full std library is available
Source validation (validate_rust_source()) rejects:
fn main— harness provides it#![...]— crate-level attributes not supported
Comment stripping (strip_comments()) reduces false positives from patterns in comments.
Workspace
Workspace is a shared project context for multi-agent collaborative code generation. Specialist agents coordinate through typed CollaborationEvents with ownership, correlation, and wait/resume semantics.
use ;
use Duration;
let ws = new
.project_name
.session_id
.build;
// Request work from another specialist
ws.request_work;
// Publish completed work
ws.publish_work;
// Wait for correlated response
let result = ws.wait_for_work.await;
Convenience methods on Workspace:
| Method | Event Kind |
|---|---|
request_work() |
NeedWork |
claim_work() |
WorkClaimed |
publish_work() |
WorkPublished |
request_feedback() |
FeedbackRequested |
provide_feedback() |
FeedbackProvided |
signal_blocked() |
Blocked |
signal_completed() |
Completed |
Wait methods: wait_for(correlation_id, timeout), wait_for_work(), wait_for_feedback(), wait_for_kind(correlation_id, kind, timeout).
The WorkspaceBuilder supports project_name(), session_id(), created_at(), and channel_capacity() (default 256).
A2A Collaboration Transport
The a2a_compat module provides a CollaborationTransport trait abstracting the event transport layer. Phase 1 uses LocalTransport (in-process broadcast channel). The event model maps cleanly onto the ADK A2A protocol for future remote specialist execution:
| Collaboration Concept | A2A Concept |
|---|---|
CollaborationEvent |
A2A Message or TaskStatusUpdateEvent |
correlation_id |
A2A task_id |
producer / consumer |
A2A agent card sender / receiver |
NeedWork |
Submitted |
WorkClaimed |
Working |
WorkPublished |
Completed + artifact |
FeedbackRequested |
InputRequired |
Completed |
Completed (final) |
Error Types
Two error enums:
ExecutionError — legacy executor errors:
| Variant | Description |
|---|---|
UnsupportedPolicy |
Backend cannot enforce a requested sandbox control |
UnsupportedLanguage |
Backend does not support the requested language |
CompileFailed |
Compilation failed |
Timeout |
Execution exceeded timeout |
ExecutionFailed |
Runtime failure |
Rejected |
Rejected before running (policy/scope check) |
InvalidRequest |
Malformed request |
InternalError |
Thread panic or unexpected failure |
CodeError — new pipeline errors with structured diagnostics:
| Variant | Description |
|---|---|
CompileError |
Compilation errors with Vec<RustDiagnostic> and raw stderr |
DependencyNotFound |
Required dependency (e.g., serde_json) not found |
Sandbox |
Underlying SandboxError from the backend |
InvalidCode |
Source code invalid before compilation |
Both implement From<...> for AdkError with appropriate component/category/code mappings.
Migration from Previous API
The previous adk-code API (CodeExecutor, ExecutionRequest, RustSandboxExecutor, etc.) is deprecated. See the compat module for the full migration table.
| Old Type (deprecated) | New Type | Crate |
|---|---|---|
CodeExecutor |
SandboxBackend |
adk-sandbox |
ExecutionRequest |
ExecRequest |
adk-sandbox |
ExecutionResult |
ExecResult |
adk-sandbox |
RustSandboxExecutor |
RustExecutor |
adk-code |
RustSandboxConfig |
RustExecutorConfig |
adk-code |
RustCodeTool (adk-tool) |
CodeTool |
adk-code |
Key API changes:
SandboxBackendhas no lifecycle methods — justexecute(ExecRequest)ExecRequestis flat:language,code,stdin,timeout,memory_limit_mb,envRustExecutor::new(backend, config)takes aSandboxBackendinstead of embedding isolation- Deprecated aliases compile with warnings for one release cycle (removed in v0.6.0)
License
Apache-2.0