Claude
Agent
environments
Run shell and filesystem tools in local, Docker, Hugging Face, or custom environments.
What vetted this — trust report
Download
evalstate-fast-agent-docs_docs_agents_environments.md-9be5169.zip · 5 KB
Install
skills CLI
npx skills add https://github.com/evalstate/fast-agent/tree/main/docs/docs/agents/environments.md
Git
git clone https://github.com/evalstate/fast-agent.git
The skills CLI installs just this skill, for any of its supported agents. Git is the plain clone.
Files (fast-agent)
-
environments.md 18 KB
--- title: Execution Environments description: Run shell and filesystem tools in local, Docker, Hugging Face, or custom environments. social: title: Execution Environments tagline: Set up local, containerised and remote sandboxes where harness shell commands run. description: Run shell and filesystem tools in local, Docker, Hugging Face, or custom environments. alt: fast-agent social card — Execution Environments --- # Execution Environments Execution environments define where your agent runs shell commands. Configure reusable environments in your fast-agent config file, then select them by name from the Python API or the CLI. !!! note Do not confuse an execution environment with your workspace or fast-agent home. The workspace is the project file tree for a run; the home is fast-agent's local config and state root, usually `<workspace>/.fast-agent`. By default, fast-agent uses the implicit `local` environment. To start fast-agent with local shell access simply use `fast-agent -x`. You can switch to Docker, a Hugging Face Sandbox, or a custom adapter without changing agent definitions. ```python result = await harness.shell("pwd") print(result.stdout, result.stderr, result.exit_code) ``` ## Simple Setup The easiest way to configure environments is to prompt **`fast-agent`**: ```bash # container fast-agent -xx \ -m "configure a docker execution environment (ubuntu) named docker-env " \ " with a read only mount of the current working directory. make it the default" \ --model codexplan ``` ```bash # hf sandbox fast-agent -xx --url https://huggingface.co/mcp?bouquet=files \ -m "i want to set up an execution environment (hf sandbox) with my most " \ " recent dataset attached" \ --model codexplan ``` ## Named environments Add `environments:` to `<home>/fast-agent.yaml`. Relative mount sources resolve against the workspace. ```yaml default_environment: ubuntu environments: ubuntu: type: docker image: ubuntu:24.04 shell: bash cwd: /workspace mounts: - source: . target: /workspace mode: rw hf-gpu: type: huggingface image: python:3.12 flavor: cpu-basic cwd: /workspace volume_mounts: - hf://buckets/username/my-bucket:/workspace:rw staging: type: custom class: mycompany.envs:KubernetesEnvironment params: namespace: agents-staging ``` Use the configured name wherever an execution environment is accepted: ```python async with fast.harness(environment="ubuntu") as harness: result = await harness.shell("pwd") async with fast.run(environment="hf-gpu") as agent_app: await agent_app.interactive() ``` From the CLI: ```bash fast-agent go --environment ubuntu -x ``` Omitting `environment=` uses `default_environment`; if no default is configured, `local` is used. ## Which file tools do agents get? File tools are workspace tools: `read_text_file`, `write_text_file`, `edit_file`, and `apply_patch` are exposed only when they operate on the same tree the shell sees. `edit_file` creates a missing text file when `old_string` is omitted or empty, including missing parent directories. Creation never overwrites an existing path; exact replacement still requires a non-empty `old_string`. | Runtime | File tools | | ------- | ---------- | | ACP client with file capabilities | Client workspace tools, with local gap-fill for missing edit/patch tools. | | Active environment implements `EnvironmentFilesystem` | Environment filesystem, including local, mounted Docker, and Hugging Face Sandbox environments. | | Plain local shell with no injected environment object | Host workspace filesystem. | | Shell-only environment | No model-facing workspace file tools. | There is no host fallback for a remote/container shell. If Docker runs in `/workspace`, file tools must target the same mounted `/workspace` tree, not an unrelated host path. ## Environment variables fast-agent does not copy your host process environment wholesale into Docker containers or Hugging Face Sandboxes. Environment variables are opt-in: - `env:` on the configured environment supplies default variables for shell commands in that environment. - Per-call `env` passed to `harness.shell(..., env={...})` or `ShellExecutionRequest.env` is merged over the configured defaults for that command. - Provider credentials used by fast-agent itself, such as the Hugging Face `token:` used to create a Sandbox, are not automatically exposed inside the environment as command environment variables. Use `${...}` references with `fast-agent.secrets.yaml` or host environment variables when you intentionally want a value to be sent into the execution environment: ```yaml environments: ubuntu: type: docker image: ubuntu:24.04 cwd: /workspace env: APP_ENV: development API_TOKEN: ${MY_APP_TOKEN} hf: type: huggingface image: python:3.12 cwd: /workspace token: ${HF_TOKEN} # used by fast-agent to create/manage the Sandbox env: APP_ENV: development DATASET_TOKEN: ${MY_DATASET_TOKEN} # visible to commands in the Sandbox ``` ```python async with fast.harness(environment="ubuntu") as harness: result = await harness.shell( "printf '%s\n' \"$APP_ENV:$RUN_ID\"", env={"RUN_ID": "manual-test"}, ) ``` Treat anything under `env:` as visible to commands running in that environment. ## Skills follow the environment Agent Skills are discovered from the active environment's filesystem, so skill paths shown to the model are always readable by its file tools and skill scripts are executable by its shell. Local runs scan the host as usual. For a non-local environment, mount (or copy) your skills into it — the default discovery paths (`.fast-agent/skills`, `.agents/skills`, `.claude/skills`) resolve against the environment working directory: ```yaml environments: ubuntu: type: docker image: ubuntu:24.04 mounts: - source: . target: /workspace mode: rw - source: .fast-agent target: /workspace/.fast-agent mode: ro ``` An environment without a filesystem cannot surface skills; if skills are configured, fast-agent warns at startup and runs without them. In the interactive UI, `/skills` lists locally installed skills. When the active environment is remote or containerized, fast-agent adds a warning because those local skills may not be present in the environment. Use `/system` to inspect the resolved prompt and confirm which skills the agent can actually read. ## Copying files between environments Harness code always has a host-side local environment at `harness.local` alongside the active environment. Use transfer helpers for explicit staging and artifact collection: ```python from fast_agent.tools.environment_transfer import copy_tree async with fast.harness(environment="hf-gpu") as harness: await copy_tree(harness.local, "datasets/input", harness.environment, "/workspace/input") session = await harness.session("train", agent_name="researcher") await session.generate("Train on /workspace/input and write metrics to /workspace/out") await copy_tree(harness.environment, "/workspace/out", harness.local, "results") ``` !!! note "Sandbox" is adapter-specific. Docker and remote providers may offer useful isolation, but fast-agent does not claim a universal sandbox security contract. Treat mounted files, credentials, and network access as explicit product choices. ## Defining environments in code Pass a `ShellEnvironment` to `FastAgent.harness(...)` for programmatic shell calls. If the object also implements `EnvironmentFilesystem`, shell-enabled agents created under the harness use the same environment for model-facing file tools. ```python from pathlib import Path from fast_agent.tools.docker_shell_environment import ( DockerManagedShellEnvironment, DockerMount, ) environment = DockerManagedShellEnvironment( image="ubuntu:24.04", shell="bash", cwd="/workspace", mounts=[DockerMount(source=Path.cwd(), target="/workspace", mode="rw")], ) async with fast.harness(environment=environment) as harness: result = await harness.shell("pwd") assert result.stdout.strip() == "/workspace" ``` The harness calls `environment.open()` on entry and `environment.close()` on exit. `HarnessSession.shell(...)` delegates to the same environment. ## Interactive agents You can also pass an environment to `FastAgent.run(...)`. This affects model-facing shell tools for shell-enabled agents, including normal interactive sessions. ```python async with fast.run(environment=environment) as agent_app: await agent_app.interactive() ``` If an agent exposes the `execute` tool, model tool calls run in the injected environment. This lets you use the normal fast-agent interactive UI while keeping shell commands inside Docker or another adapter. Manual example: ```bash uv run python examples/docker-shell/interactive.py ``` ## Docker environments fast-agent includes Docker shell adapters and a mounted environment adapter: | Adapter | Use when | | ------- | -------- | | `DockerManagedShellEnvironment` | fast-agent should start and remove a disposable container for the harness run. | | `DockerShellEnvironment` | You already have a running container and want fast-agent to execute commands inside it. | | `DockerMountedEnvironment` | You want Docker `execute` and model-facing file tools to target the same bind-mounted workspace. | Managed Docker example: ```python from pathlib import Path from fast_agent.tools.docker_shell_environment import ( DockerManagedShellEnvironment, DockerMount, ) environment = DockerManagedShellEnvironment( image="ubuntu:24.04", cwd="/workspace", mounts=[DockerMount(Path.cwd(), "/workspace")], ) ``` Mounted environment example: ```python from pathlib import Path from fast_agent.tools.docker_shell_environment import DockerMountedEnvironment environment = DockerMountedEnvironment( image="ubuntu:24.04", workspace=Path.cwd(), target="/workspace", ) ``` Existing container example: ```python from fast_agent.tools.docker_shell_environment import DockerShellEnvironment environment = DockerShellEnvironment( container="fast-agent-workspace", cwd="/workspace", shell="bash", ) ``` The Docker adapters run commands with `docker exec -w <cwd> ... <shell> -lc <command>` for POSIX-style shells, or PowerShell flags for `pwsh`/`powershell`. Environment variables passed to `harness.shell(..., env={...})` are forwarded with Docker `-e NAME` arguments and inherited from the Docker CLI subprocess environment, so values are not placed directly in the command argv. Managed Docker containers are started without the configured `env:` on their long-lived `sleep infinity` process; the configured and per-command variables are applied to each `docker exec` command. `DockerMountedEnvironment` maps file tool paths under `target` back to the host bind mount, so `execute`, `read_text_file`, and `apply_patch` all operate on the same visible tree. ## Hugging Face Sandbox environments `HuggingFaceSandboxEnvironment` runs both shell execution and model-facing file tools inside a Hugging Face Sandbox. Configured environments can mount Hub volumes with the same `hf://...:/mount/path[:ro|:rw]` syntax used by the Hub CLI: ```yaml environments: hf-gpu: type: huggingface image: python:3.12 flavor: cpu-basic cwd: /workspace volume_mounts: - hf://buckets/username/my-bucket:/workspace:rw - hf://datasets/username/reference-data:/data:ro ``` At the Python API layer, bucket mounts can also be constructed explicitly: ```python from fast_agent.tools.huggingface_sandbox_environment import ( HuggingFaceBucketMount, HuggingFaceSandboxEnvironment, ) environment = HuggingFaceSandboxEnvironment( image="python:3.12", flavor="cpu-basic", cwd="/workspace", bucket_mounts=( HuggingFaceBucketMount( source="username/my-bucket", mount_path="/workspace", read_only=False, ), HuggingFaceBucketMount( source="username/reference-data", mount_path="/data", read_only=True, ), ), ) ``` Manual example: ```bash uv run python examples/huggingface-sandbox/interactive.py \ --bucket username/my-bucket:/workspace:rw ``` The config surface currently creates dedicated Sandboxes with `huggingface_hub.Sandbox.create`. Hugging Face `SandboxPool` pooling is not exposed through `fast-agent.yaml`; if you need pooled sandbox lifecycle today, wrap the Hub pool in a custom `ShellEnvironment`/`EnvironmentFilesystem` adapter and configure it as `type: custom`. For Hugging Face Sandboxes, configured `env:` is passed to `Sandbox.create(env=...)` and also merged into each `sandbox.run(...)` call. Per-command env overrides are added for that command only. The `token:` field is used by fast-agent to authenticate with the Hub API; it is not automatically forwarded as `HF_TOKEN` inside the Sandbox. If commands inside the Sandbox need `HF_TOKEN`, add it explicitly under `env:`. ## Docker and Hugging Face examples There are example configurations and programs in `examples/environments`. Docker is not required for the automated test suite. To manually exercise the managed Docker adapter: ```bash uv run python examples/docker-shell/docker_shell_harness.py ``` The example creates a temporary host workspace, mounts it into an Ubuntu container at `/workspace`, runs `harness.shell(...)`, and verifies a container write is visible on the host. ## CWD semantics `cwd` is a string at the environment protocol boundary: - local environments interpret it as a host path; - Docker environments interpret it as a container path; - remote environments should interpret it as a provider-side path. This keeps container paths like `/workspace` from being coerced into host `Path` objects. Environment objects may be shared across agents and harness sessions. Treat `cwd` as adapter-level default state and pass per-agent or per-call working directories through `AgentConfig.cwd`, `harness.shell(..., cwd=...)`, or `ShellExecutionRequest.cwd`. ## Implementing a custom environment Custom environments implement `ShellEnvironment` from `fast_agent.tools.execution_environment`. ```python from fast_agent.tools.execution_environment import ( EnvironmentFileEntry, ShellEnvironment, ShellExecution, ShellExecutionOptions, ShellExecutionRequest, ShellExecutionResult, ShellRuntimeInfo, ) class MyEnvironment: async def open(self) -> None: ... @property def cwd(self) -> str: return "/workspace" def runtime_info(self) -> ShellRuntimeInfo: return ShellRuntimeInfo(name="bash", kind="remote", provider="my-provider") async def execute(self, request: ShellExecutionRequest, *, callbacks=None) -> ShellExecution: result = await my_provider_exec( request.command, cwd=request.cwd or self.cwd, env=dict(request.env or {}), timeout=request.timeout, ) return ShellExecution( result=ShellExecutionResult( stdout=result.stdout, stderr=result.stderr, exit_code=result.exit_code, ), options=ShellExecutionOptions(timeout_seconds=request.timeout), ) async def close(self) -> None: ... ``` Adapters that can stream output should call callback hooks as output arrives: - `on_stdout(text)` - `on_stderr(text)` - `on_idle_warning(elapsed, remaining)` - `on_timeout()` If a provider cannot stream, ignore callbacks and return final output. To give the LLM natural file access to the same environment, implement `EnvironmentFilesystem` on the same object: ```python from fast_agent.tools.execution_environment import EnvironmentFilesystem class MyEnvironment: # ShellEnvironment methods above... def resolve_path(self, path: str) -> str: ... async def read_text(self, path: str) -> str: ... async def write_text(self, path: str, content: str) -> None: ... async def exists(self, path: str) -> bool: ... async def list_dir(self, path: str) -> list[EnvironmentFileEntry]: ... async def mkdir(self, path: str) -> None: ... async def remove(self, path: str) -> None: ... ``` Keep provider-specific concepts inside the adapter. For example, Hugging Face bucket mounts belong on `HuggingFaceSandboxEnvironment`, while the generic runtime only depends on `ShellEnvironment` and `EnvironmentFilesystem`. Custom environments can also opt into temporary subagent transcripts by implementing `EnvironmentTemporaryArtifacts` on the same object. Its `write_temporary_text(...)` operation must allocate an unpredictable private file and perform the bounded write as one adapter-level operation. The returned path must be visible to that environment's shell and file tools. ```python from fast_agent.tools.execution_environment import ( EnvironmentTemporaryArtifacts, TemporaryArtifact, ) class MyEnvironment: # ShellEnvironment and optional EnvironmentFilesystem methods above... async def write_temporary_text( self, *, prefix: str, suffix: str, content: str, max_bytes: int, ) -> TemporaryArtifact: ... async def remove_temporary_artifact( self, artifact: TemporaryArtifact, ) -> None: ... ``` Do not implement this capability with a predictable path followed by a generic file write, or place artifacts on persistent mounts. fast-agent treats artifact creation and cleanup as best effort; environments without the capability keep the final-response-only subagent behavior. `ShellRuntimeInfo.kind` is coarse display metadata. Built-in values include `local`, `docker`, and `remote`, but custom providers can use another stable string and should set `provider` to the adapter name. For Skills, fast-agent scans the active environment filesystem when one is available, and formats skill paths for the environment `read_text_file` tool. Local runs scan the host workspace/home paths. If an environment has shell execution but no filesystem contract, Skills are not surfaced for that run.
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.