Code Execution Provider Migration
July 19, 2026 · View on GitHub
Languages: English · 中文
This guide is for provider contributors. Agently's base implementation owns the provider-neutral execution contract; an isolation implementation remains owned, reviewed, and tested in the contributor's pull request.
Fixed ownership and call order
code_execution is the resource kind. Docker, a container runtime variant, a
host policy mechanism, a remote worker, and the explicit unsafe local runner are
provider implementations, not new resource kinds.
Every call follows this order:
TaskWorkspace
-> issue TaskWorkspaceAccessGrant
-> select and bind ExecutionResourceProvider
-> materialize immutable CodeExecutionBundle
-> execute the adapter-owned argv plan
-> collect declared outputs into TaskWorkspace
-> release provider
-> close grant
The provider receives the exact grant, bundle, and materialization manifest. It must not accept an inline-source bypass or construct mounts/policies from model-produced paths.
Required provider surface
A preferred provider exposes:
- a stable
provider_idandsupported_kinds = ("code_execution",); async_probe(...)with observed availability and capability facts;async_ensure(...),async_health_check(...), andasync_release(...);- a resource implementing
async_execute_code(bundle, manifest, grant, timeout); - Workspace-root translation derived only from
TaskWorkspaceAccessGrant; - argv-only execution, bounded output, declared-output readback, and cleanup.
async_probe(...) should report supported languages, observed toolchain
versions, Workspace access modes, isolation, safety class, network behavior,
and mechanism-specific facts. Provider selection is ordered and deterministic;
unavailable, version-ineligible, or capability-ineligible candidates are
recorded and skipped. A hard isolation="required" request can never select
trusted_local. The selected provider facts are attached to Action result
metadata; do not infer safety from provider names.
For code_execution, capabilities["isolation"] is a mapping of observed
boolean axes (process_contained, host_filesystem_restricted,
privilege_escalation_blocked, and syscalls_restricted) plus optional
mechanism facts. Legacy string labels are rejected. Providers must also bound
captured output, stop their process/container on timeout and coroutine
cancellation, and make async_release(...) failures visible; the manager
quarantines a failed handle instead of declaring it released.
Application configuration may provide strings or candidate descriptors:
agent.settings.set(
"code_execution.providers",
[
{"provider_id": "preferred-provider", "config": {"profile": "strict"}},
"docker",
],
)
agent.enable_code_runtime(language="python")
Candidate configuration is merged only for the candidate being probed or ensured. This lets two candidates use different mechanisms without leaking provider-specific settings into the core Action contract.
Container-runtime variants can subclass DockerExecutionResourceProvider and
override only create_resource(...) to construct their own
DockerExecutionResource subclass. ExecutionResourceManager still owns
ordered selection and the mandatory re-probe before ensure; the inherited
Docker provider reuses Workspace-grant binding, image preparation, health
checks and cleanup. The variant still owns and must test its runtime-specific
probe facts and command construction; the factory is not permission to inject
model-produced Docker arguments.
Refactor target for PR #325
The gVisor contribution remains owned by its contributor. Rebase or retarget the
PR after the base contract lands, then adapt it as a code_execution provider
or a composition of the Docker resource. The refactored PR should:
- probe the configured Docker binary, daemon, and requested runtime for real;
- report the active runtime in probe/handle facts;
- construct the runtime-specific resource through
create_resource(...)instead of copying the Docker provider lifecycle; - derive mounts from the Workspace grant;
- consume adapter-produced build/run steps and immutable source bytes;
- fail closed when the requested runtime is absent, without silently using the default Docker runtime;
- retain its concrete runtime implementation and real tests in PR #325.
The base branch intentionally contains no copied gVisor commands or provider implementation.
Refactor target for PR #327
The Seatbelt contribution also remains contributor-owned. Refactor it as a
code_execution provider with its own stable provider_id. The PR should:
- probe the actual platform and policy executable;
- generate policy from resolved grant roots, never from arbitrary extra rules;
- keep source read-only and build/output/log roots writable as granted;
- use async argv-only process execution with bounded stdout/stderr;
- validate realpath containment and remove temporary policy files on every success, failure, timeout, and cancellation path;
- retain its concrete profile implementation and real tests in PR #327.
The base branch intentionally contains no copied Seatbelt profile or provider implementation.
Pull-request acceptance checklist
- No alternate Workspace, sandbox manager, session lifecycle, or resource kind.
- No raw source path, raw command, or provider-specific mount in model-visible Action input.
- Probe facts are observed; synthetic fixtures are labeled as such.
- Toolchain facts use canonical tool ids (
python,node,go,c++) and normalized observed versions so minimum/exact constraints can be enforced. - The provider passes the generic external-provider contract tests plus its own real mechanism tests.
- Documentation names the safety class and fallback behavior explicitly.