Core Concepts
The nine nouns agent-env is built from, and how a run threads through them
agent-env is built from a small fixed vocabulary: artifacts hold the bytes, an env turns them into a running sandbox, and a task is a DAG of steps that deploys that sandbox, drives an agent through it, and grades what happened. Every other page on this site uses these words exactly as defined here; if you have not run anything yet, read Getting Started first and come back.
Artifact
An artifact is an immutable, versioned input — a Docker image, a file, a dataset.
Each artifact is a document in the configured document store,
and most also carry a blob in the object store under
artifacts/<type>/<id>/<version>/<name>. put() never mutates anything: it allocates the
next integer version for that id (versions start at 1; an artifact that has never been
stored has version = 0), inserts a new document, and relies on a unique (id, version)
index to reject any write over an existing version. The concrete types are
DockerImageArtifact, FileArtifact, VMImageArtifact, EnvironmentArtifact (one env's
seed data), SkillArtifact, CliArtifact, and the two universes below —
see Artifacts.
Universe
A universe is an artifact whose payload is a set of ArtifactRefs: frozen (id, version)
pointers to other artifacts. That pinning is what makes a world reproducible — a universe is
the unit of world state you hand a task, and re-running it a month later loads the same bytes.
Two kinds are concrete: EnvironmentUniverseArtifact bundles EnvironmentArtifact refs plus
optional metadata files, and FileArtifactUniverse maps an original filename to a pinned
FileArtifact ref. The rule for choosing: an environment universe when the data must be
ingested into the services an env instance runs, a file artifact universe when the payload is
files to stage onto a sandbox or files a run produced. Universe itself is abstract and
raises TypeError on instantiation — see Universes.
Env
env, environment, and env instance
This site uses environment as the ordinary English word — in section titles, page
descriptions, and introductions — and env as the technical noun for the stored definition,
matching the Env class, the envs collection, and the agent-env env command group. They
name the same thing. The deployment is always an env instance, never a "deployed env".
An env is a versioned definition: an id, a version, a type key, and the artifacts it
is built from. An MCPServerEnv document, for example, holds a pinned DockerImageArtifact
ref and the environment_name that image serves. Loading one resolves polymorphically through
the env registry on the document's type field, so an unknown type fails loud with
Unknown env type; the built-in types are mcp_server, multi, service_db, website,
gateway_server, remote_mcp_endpoint, and coding_task_harbor. Storing an env starts
nothing — see Environment types.
Env instance
An env instance is what deploy() produces: a live sandbox running the env's docker
compose stack — an MCP gateway plus a container per service the env declares (one per MCP
server, plus the website or database containers an env type brings with it) — described by a
DeployedEnv record with an instance_id of the form <env_id>-<8 random chars>, a
gateway_url, an mcp_url, a sandbox_id, and a created_at_utc/expires_at_utc window.
The TTL is handed to the sandbox provider as the sandbox's own timeout, so an instance tears
itself down whether or not the run that created it finished. One definition can have many
instances alive at once, and deploying never writes back to the definition.
Definition versus instance
my-env names a definition; my-env-4f2a9c1b names one deployment of it. agent-env env deploy --id my-env mints an instance and agent-env env get-instance --id my-env-4f2a9c1b
reads it back. remote_mcp_endpoint envs have no instance at all — their deploy() raises
NotImplementedError, because they point at a live URL you already run, and are referenced
from deploy_agent. See Lifecycle.
Task
A task is a named, versioned list of TaskSteps stored inline in one document, executed as
a DAG whose edges come from each step's depends_on. Task.run() walks that DAG with
asyncio, so steps with no unmet dependencies run concurrently. The constructor rejects
duplicate step ids and forward references — every depends_on must name a step that appears
earlier in the list. A step's fail_task_on_error (default True) decides whether its
failure halts the run — no further steps are scheduled, and already-running siblings are
allowed to finish — or is recorded and stepped over.
Omitting depends_on is not the same as an empty list
"depends_on": [] means no dependencies — the step is eligible to start immediately.
Omitting the key entirely (None) means the step depends on every prior step in the list,
which makes the task sequential. This is the most common reason a task that looks parallel
runs one step at a time.
TaskStep
A TaskStep is one unit of work with a single abstract method,
async execute(context) -> context. Steps are polymorphic by type key through the task
step registry, the same pattern envs and artifacts use, and the registry is where custom
steps get plugged in (Custom types). A step may also implement
preflight(), which returns one message per detectable config problem and must answer
without a sandbox or an env instance. The built-in set runs to several dozen types in seven
families — provisioning, data staging, env control, execution, capture, grading, and
registration validators — grouped in Tasks and steps and enumerated in the
Task step reference.
TaskStepContext
The TaskStepContext is the one mutable object threaded through every step in a run:
agent-env hands the same object to every execute() call, and a step publishes its output by
mutating it in place. execute() returns the context by convention, but Task.run() discards
the return value and keeps using the object it passed in. This is how steps communicate — a
step reads what an earlier step put there, and nothing else is shared. Its fields are
deployed_envs, deployed_agents, deployed_sandboxes, prompt_responses, metadata,
agent_model, default_agent_model, agent_artifact_id, agent_harness, and
instance_id. metadata is the open dict where conventional keys accumulate — seed,
user_overrides, verifications, failed_steps — and to_safe_dict() recursively strips
credential keys before the context is serialized anywhere.
Several layers can name the model an agent runs with. A run-level override
(Task.run(agent_model=...), or agent-env task run --agent-model) beats the model field
stored on the prompt_agent step, which beats context.default_agent_model — the value
deploy_agent or install_agent stamps from the agent's declared default_model or the
agent model role in your config file, and only if nothing has set it yet. The full override
table is in Tasks and steps; the endpoint and credential precedence behind it is
in Configuration.
Eval
An eval is a versioned list of pinned task references — (task_id, task_version) pairs,
where a task_version of None resolves to the latest at run time. It holds no execution
logic of its own: running an eval loads each referenced task and fans out Task.run() k
times per task, optionally under a concurrency limit. Reach for a task when you want one
scenario, an eval when you want a suite run repeatedly for pass@k — see
Evals.
Trajectory
A trajectory is the recorded agent run: the raw spans an agent emits while working,
captured as OpenTelemetry GenAI spans (gen_ai.* semantic conventions, no vendor-specific
attributes required). prompt_agent uploads the raw trajectory to the object store and puts
its URI on the PromptResponse; judges read a compacted form, which reduces the spans to an
event list and externalizes oversized or base64 tool results to side files. See
LLM rubric judging.
How a run fits together
The canonical pipeline is six stages. Each one leaves its output on the context for the next:
deploy_envloads the env definition by(id, version), deploys it, and appends aDeployedEnvtocontext.deployed_envs. It refuses an env id already in the context.load_artifactingests a universe into that env instance, and records what it loaded undercontext.metadata["loaded_environment_universes"](orloaded_file_artifact_universeswhen staging files onto a sandbox).deploy_agentprovisions an agent sandbox wired to the named envs' MCP gateways and appends aDeployedAgenttocontext.deployed_agents.prompt_agentsends the message over A2A, uploads the trajectory, and appends aPromptResponsetocontext.prompt_responses.- A verifier grades the result and writes
context.metadata["verifications"][<verifier_id>] = {"results": [...], "score": ...}. collect_artifactspulls files off a sandbox, stores each as aFileArtifact, bundles them into aFileArtifactUniverse, and records both undercontext.metadata.
The context after such a run, annotated with the step that wrote each part:
TaskStepContext
├── deployed_envs[0] DeployedEnv <- deploy_env
│ ├── instance_id "support-env-4f2a9c1b"
│ ├── mcp_url gateway MCP endpoint the agent calls
│ └── expires_at_utc when the sandbox reaps itself
├── deployed_agents[0] DeployedAgent <- deploy_agent
│ ├── agent_name "default-agent"
│ └── a2a_url where prompt_agent sends the message
├── prompt_responses[0] PromptResponse <- prompt_agent
│ ├── response the agent's final text
│ ├── agent_trajectory_s3_uri raw OTel GenAI spans
│ └── tool_call_count
└── metadata
├── loaded_environment_universes[] <- load_artifact
├── verifications{<verifier_id>} <- any verifier
├── artifacts, file_artifact_universe <- collect_artifacts
└── failed_steps[] every step failure, with is_fatal per entryNothing forces this order. A task that deploys two envs in parallel, prompts an agent three times, or skips grading entirely is equally valid; what is fixed is the medium, because every stage communicates only through the context.
Related docs
- Tasks and steps — the DAG, the context, and the step families in detail
- Your first task — the six stages above as a runnable task
- Lifecycle, TTLs, and cleanup — deploy, TTL, and teardown in detail
- Universes — pinning, composing, and loading world state
- Configuration reference — every config key, and the precedence between them
- The extension model — the four ways to plug your own classes in
- Custom types — registering your own env, step, or artifact