Skip to content

Latest commit

聽

History

History
382 lines (309 loc) 路 16.1 KB

execution-environment.md

File metadata and controls

382 lines (309 loc) 路 16.1 KB
title Execution Resource
description Managed execution resources for Actions and TriggerFlow.
keywords Agently, ExecutionResource, Action, TriggerFlow, sandbox, MCP, runtime_resources

Execution Resource

For general Bash/PowerShell source, see Shell: agent.enable_shell(environment=..., approval=...) uses the managed shell resource kind.

Languages: English 路 涓枃

Renamed in the 4.1.3.8 TaskWorkspace/ActionRuntime boundary refactor: the managed live-resource seam is now ExecutionResource (ExecutionResourceManager, ExecutionResourceProvider, Agently.execution_resource). The previous ExecutionEnvironment* names are removed. This page keeps its URL for link stability.

Execution Resource is the framework-level layer that prepares and releases managed execution resources before an action or workflow step runs.

It owns lifecycle and policy for resources such as MCP transports, command runners, sandboxes, browsers, SQLite connections, and external process runners. Action and TriggerFlow can require those environments, but they do not own environment lifecycle.

Audience

Most application developers should not start here. Prefer built-in actions and Agent Component helpers that describe intent, such as enabling Python, shell, workspace, MCP, SQLite, vector-store, or coding-workspace capabilities.

Read this page when you are:

  • writing a custom ActionExecutor that depends on a managed live resource
  • writing an ExecutionResourceProvider plugin
  • reviewing how Action or TriggerFlow receives managed resources
  • designing a new built-in capability that needs sandbox, process, MCP, client, credential, or cleanup lifecycle

Do not expose Agently.execution_resource as the default app-development mental model. It is the core lifecycle layer behind higher-level capabilities.

Where it sits

Agent Component / built-in Action / custom Action / TriggerFlow / Skills plan
        |
        v
ActionSpec.execution_resources or TriggerFlow execution requirements
        |
        v
ExecutionResourceManager
        |
        v
ExecutionResourceProvider
        |
        v
managed handle / live resource

V1 exposes the global manager as:

from agently import Agently

Agently.execution_resource

Most application code does not call the manager directly. Built-in MCP, Bash, code-execution, Docker, Browser, and SQLite actions can declare their requirements and the Action dispatcher ensures them before executor calls.

For the broader ownership model, see Architecture / Extension Boundaries.

Built-in behavior

The built-in providers are:

Kind Used by Managed resource
mcp agent.use_mcp(...) / MCP actions MCP transport resource
bash sandbox="trusted_local" shell actions configured local command runner
docker isolated shell actions, direct Docker Actions, and one code_execution provider candidate Docker CLI runner and image provisioning
code_execution agent.enable_python(...), agent.enable_nodejs(...), agent.enable_code_runtime(...), and the restricted Action enabled for the current execution by agent.enable_skill_script_exec(...) provider-neutral Workspace-bound execution; built-ins include Docker, optional gVisor/runsc, optional macOS Seatbelt, optional Linux Landlock, and the explicit unsafe trusted_local fallback
browser Browse actions that opt into managed browser resources managed browser/page/session wrapper
sqlite agent.enable_sqlite(...) / SQLite executor actions SQLite connection

Search intentionally is not listed here. It is a stateless Action-native capability package; proxy, timeout, backend, and region belong to the Search package/executor configuration rather than ExecutionResource.

These providers are low-level environment implementations. User-facing capabilities should normally be exposed as Actions, and scenario shortcuts should be exposed through Agent Components or future agent.enable_* helpers. Python and Node.js helpers are language-specific facades over the same kind="code_execution" contract used by enable_code_runtime(...); they are not separate providers. Python, Node.js, Go, and C++ differ through language adapters, while provider probes select the first installed and eligible configured execution mechanism. Hard isolation and Workspace capability checks still fail closed, and trusted_local remains explicitly unsafe.

Action execution flow:

ActionCall
  -> resolve ActionSpec
  -> issue TaskWorkspace access grant when required
  -> probe/select and ensure ActionSpec.execution_resources
  -> inject execution_resource_resources into action_call
  -> materialize immutable code bundle into TaskWorkspace
  -> ActionExecutor.execute(...)
  -> collect declared outputs
  -> release action_call-scoped handles
  -> close Workspace grant

Custom ActionExecutor.execute(...) signatures do not change. Managed handles are passed through action_call["execution_resource_handles"] and live resources through action_call["execution_resource_resources"].

Programmatic Action bindings

The programmatic Action planning protocol adds a stricter consumer of the existing code_execution kind. Its generated Python program needs a provider that both satisfies isolation="required" and reports host-binding capability. The binding bridge carries JSON calls only; it does not move credentials, live Action objects, callbacks, or policy authority into the program process.

Each binding request is reconstructed and dispatched through the ordinary ActionRuntime/ActionDispatcher path. A provider name, Python support, or isolation support alone does not imply binding support. Selection fails closed when no provider satisfies all requested capabilities and never falls back to trusted_local.

On POSIX hosts, the built-in Docker provider and its gVisor variant implement the binding bridge while keeping container networking disabled. Eligibility still depends on observed host_async_bindings and all requested hard-isolation axes, not on the provider id.

The provider, bridge, and interpreter are live resources. TriggerFlow snapshots cannot serialize a running program or resume it at an awaited binding. Save at a settled boundary and start a new program decision after recovery. See Programmatic Action Calling.

Ordered code-execution providers

Configure provider priority with strings or candidate descriptors. Descriptor configuration is merged only for that candidate:

agent.settings.set(
    "code_execution.providers",
    [
        {"provider_id": "preferred-provider", "config": {"profile": "strict"}},
        "docker",
    ],
)
agent.enable_code_runtime(language="go")

sandbox= on enable_python(...) and enable_nodejs(...) is a compatibility shortcut limited to "auto", "docker", and "trusted_local". Optional isolation mechanisms are plugins: select them through the provider-neutral providers= and isolation= options shown below. A provider-specific configuration belongs in that provider's candidate descriptor, not in the core Action API.

trusted_local executes host toolchains without isolation and accepts only a snapshot grant. It requires explicit host authorization and cannot satisfy isolation="required". unsafe_fallback=True must therefore be paired with an explicit isolation="preferred" or "none"; it is never selected implicitly.

If Docker is unavailable and the code is trusted, the explicit subprocess compatibility path is:

agent.enable_code_runtime(
    language="nodejs",
    providers=["trusted_local"],
    unsafe_fallback=True,
    isolation="none",
)

Do not use this for untrusted or model-generated code. The safe default fails closed instead of silently moving that code into a host subprocess.

The public isolation= option is selection policy, not a provider capability label. A code_execution provider must report concrete boolean isolation axes: process containment, host-filesystem restriction, privilege-escalation blocking, and syscall restriction. Required isolation matches all requested axes. Preferred isolation first searches the ordered candidate set for a full match and only then uses an eligible fallback, recording that fallback in the handle metadata. A provider name or a string such as "required" is not safety evidence.

gVisor Docker runtime

When the host Docker daemon is configured with the runsc runtime, select the optional gvisor provider through the generic code-runtime API:

agent.enable_code_runtime(
    language="python",
    providers=["gvisor"],
    isolation="required",
)

This chooses only the optional gvisor provider. Before a handle is ready, Agently verifies Docker availability, requires a registered runsc runtime, prepares the selected image under the configured image policy, and starts a bounded runsc container probe. A missing, malformed, unregistered, or non-executable runtime fails closed; this explicit choice never falls back to Docker/runc, auto, or trusted_local. The verified active runtime is retained in handle and code-execution result metadata. gVisor adds no default import dependency and does not make unsafe Docker arguments stronger safety evidence.

macOS Seatbelt

On macOS, explicitly select the optional Seatbelt provider with:

agent.enable_code_runtime(
    language="python",
    providers=["seatbelt"],
    isolation="preferred",
)

Seatbelt uses the system sandbox-exec mechanism, denies network access by default, and derives every writable filesystem rule from the TaskWorkspace grant. It never accepts raw SBPL rules or additional host write paths and never falls back to Docker or trusted_local. To keep host toolchains and dynamic libraries usable, this initial profile permits broad host reads; it therefore reports host_filesystem_restricted=false and the helper records preferred, not required, isolation. Use Docker/gVisor when host-read isolation is required.

Linux Landlock

On a Linux kernel with Landlock support, explicitly select the filesystem-only provider with:

agent.enable_code_runtime(
    language="python",
    providers=["landlock"],
    isolation="preferred",
)

Landlock runs a provider-owned helper process, applies PR_SET_NO_NEW_PRIVS and ABI-aware rules derived only from provider system/toolchain roots and the TaskWorkspace grant, then executes the adapter argv. Output, timeout, cancellation, and cleanup remain bounded by the normal process carrier. It accepts no raw rule manifests, ABI overrides, or additional host paths and never falls back to Docker or trusted_local. Landlock does not isolate processes, networks, or general syscalls, so it uses preferred isolation and reports those limitations explicitly.

Code requests declare at most 128 expected outputs. Each path is bounded, normalized, and must be under output/; missing declared outputs make the Action fail. Stdout/stderr retention is bounded, cancellation terminates the owned process group or container, and resource-release failure changes an otherwise successful Action into an error instead of reporting false success.

External isolation implementations register through the same ExecutionResourceProvider seam. See Code Execution Provider Migration.

TriggerFlow

TriggerFlow still uses runtime_resources as the compatibility surface for live execution-local resources. ExecutionResource does not rename or replace that API.

You can pass managed requirements at execution creation or start:

execution = flow.create_execution(
    execution_resources=[
        {
            "kind": "custom_runtime",
            "provider_id": "my-runtime-provider",
            "scope": "execution",
            "resource_key": "runtime",
        }
    ],
)

After the host registers the named provider, the manager ensures the resource, injects it into the execution-local resources, and releases it when the execution closes. Code execution itself should still be invoked through a Workspace-bound CodeExecution Action so bundle materialization and output readback remain in the Action Runtime chain. Manual runtime_resources={...} are unmanaged and are not health-checked or auto-released by the manager.

Direct manager API

This API is for framework, action, and plugin developers.

The manager supports:

Agently.execution_resource.declare(requirement)
Agently.execution_resource.ensure(requirement_or_id)
await Agently.execution_resource.async_ensure(requirement_or_id)
Agently.execution_resource.release(handle_or_id)
Agently.execution_resource.release_scope("session", owner_id)
Agently.execution_resource.inspect(id)
Agently.execution_resource.list(scope="execution")
Agently.policy_approval.register_handler("my_handler", handler)
Agently.configure_policy_approval(handler="my_handler")
Agently.set_settings("access_control_policy.auto_allow", True)

Declaration is lazy. It validates and records a requirement but does not start anything. ensure(...) starts or reuses a handle subject to policy and approval. Approval is resolved through the framework-wide Agently.policy_approval handler. The default input_timeout_fail handler prompts only in an interactive CLI and denies after timeout or immediately in non-interactive services. Service wrappers around TriggerFlow executions should register their own handler, for example one that stores a pending approval and resumes with continue_with(...). Trusted hosts can set access_control_policy.auto_allow=True through settings to approve policy gates automatically; this does not bypass provider, sandbox, path, command, or network constraints encoded in the requirement policy. Before reusing a ready handle, the manager calls provider.async_health_check(handle). Healthy handles are reused with ref_count + 1; unhealthy handles emit execution_resource.unhealthy, are released, and then a fresh handle is ensured. V2 intentionally does not add a background scheduler, lease TTL, or automatic reconnect loop.

If you are building an application, first check whether a built-in action or Agent Component already exposes the capability you need.

Observation

The manager emits framework events in the execution_resource.* family:

  • execution_resource.declared
  • execution_resource.approval_required
  • execution_resource.ensuring
  • execution_resource.probed
  • execution_resource.progress
  • execution_resource.ready
  • execution_resource.unhealthy
  • execution_resource.releasing
  • execution_resource.released
  • execution_resource.failed

Payloads include stable ids, sanitized provider/image self-check facts, bounded preparation progress, status, stable error codes, and actionable guidance. They must not include raw credentials, environment variables, command secrets, or live resource objects. With debug=True, these events form a concise environment narrative; debug="detail" also shows bounded probe and preparation payloads. An authorized image_pull_policy="if_missing" displays Docker pull progress before the Action starts executing. Simple mode uses human-facing labels such as checking environment, downloading image, and image ready rather than exposing internal provider= or phase= fields. Repeated Docker layer updates use compact lines, while start, completion, failure, and final readiness retain full stage blocks. Detail mode leads with the same readable explanation and adds the sanitized fields under Diagnostics.

Examples

Runnable examples are available in examples/execution_resource. Start with the trusted-local agent.enable_python(..., sandbox="trusted_local") quickstart when no Docker service is available, then move to the Docker-backed Ollama, DeepSeek, and common-language code runtime examples. The TriggerFlow example is intended for workflow or framework developers who need managed execution-local resources.

See also