Skip to main content
An Agent Environment is the execution context in which an agent acts and receives observations. Its taskset supplies tasks and its verifier judges completion. The current publication format bundles a taskset with an environment manifest, owned and versioned as <owner>/<name>@<version>. The manifest declares how to connect or run the environment; the bundle itself is not a running sandbox.

Layout

Everything under the directory is uploaded. Versions are immutable. Only deployment: local requires tasks.jsonl; remote services and hosted images own their task data.

tasks.jsonl

One JSON object per line:
train | eval
default:"train"
Which half of the taskset this row belongs to. Declared per row.The platform sets which half a job sees from the operation itself, so a training job cannot be handed the held-out tasks and an evaluation cannot ask for the training ones. An evaluation against a split the environment never declared is refused rather than quietly served the training tasks — that is the one failure this field exists to prevent, and it is the one that looks like a good score.
A taskset is never handed out. No route returns a copy, and there is no export. What a business considers hard is often more revealing than its training data, so a job references an environment and receives task content only inside the approved runtime.

manifest.json

string
required
Always tuneplane/environment/v1.
nemo-gym | openenv
required
The interaction contract. OpenEnv uses reset / step / state; NeMo Gym uses trajectory sessions.
local | remote | container
required
Who starts the environment implementation. local runs inside the job, remote connects to an existing service, and container asks the executor to host an image. NeMo Gym currently supports only local. Schema v1 and the former compound protocol names are rejected.
object
required
{"kind": "rubric|plugin|endpoint", "ref": "..."} — what decides whether a task was completed. See verifiers.
array
What the agent may call: a name, an endpoint, and a JSON Schema for the arguments.
object
{"plugin": "<owner>/<name>", "version": "...", "entrypoint": "module:ClassName", "digest": "..."}. Required for nemo-gym. The code belongs to a capability: environment plugin; the platform writes at most a two-line shim that imports it.
bool
default:"false"
True when the harness runs model-generated code. A job that needs one, on a deployment with no sandbox provider configured, is refused — it does not fall back to running the code in the training container.
string
default:"other"
NeMo Gym’s metrics-grouping category. Required and validated for nemo-gym, meaningless elsewhere. One of math, coding, agent, knowledge, instruction_following, long_context, safety, games, translation, e2e, rlhf, other.
string
The OCI image for an openenv environment. Resolved as a pinned runtime artifact like every other image; never pulled ad hoc.
url
Only valid with deployment: remote. Use either a fixed endpoint, a Fleet, or registered runners. Do not specify both endpoint and Fleet.

Protocol and deployment

Two interaction contracts and three deployment choices describe four supported combinations. A sandbox provider such as Docker or E2B is a separate choice; it is not a protocol value.
Turn-level. The platform runs the environment server from the taskset it holds, and the trainer drives the episode itself through openenv-core’s client: reset, step, state.Used by TRL, SkyRL, Unsloth and Axolotl. No harness needed — the trainer already owns the loop.

Reaching a remote environment

An openenv (deployment: remote) environment only works if the training job can open a connection to it. The control plane resolves where it answers and hands that address over; it never carries the episode itself (ADR-0025). An episode is a session of turns, one per generation round, and routing it through the process that decides admission would put a hop nowhere near the data on every one of them. So when the environment sits on a network the cluster cannot reach, one of these has to be true: What none of them is: proxying through the console. If you find yourself wanting that, the answer is usually the first row.
Or remove the question. An openenv (deployment: container) environment runs in a container beside the job, so there is no distance to cross and none of the three rows applies. Where the environment can be containerised, that is the route to reach for — and it is where the wider OpenEnv ecosystem is heading. It needs a kubernetes fleet today.

The harness

The code that drives a model through an environment, supplied as a capability: environment plugin. Needed for the materialised route, where the framework imports and runs it. Not needed for the served turn-level route, where the trainer drives the episode itself.
plugin.yaml
harness.digest in the manifest pins the harness package the environment was authored against. Leave it empty when a team ships the harness alongside the environment; set it when the two are versioned apart and a mismatched harness would score differently. It is verified at submission, never at rollout.

Push and reference

<owner>/<name> or bare name
required
A bare name goes into your own namespace.
string
required
Immutable once pushed.
flag
Only applies when the environment is first created.
The same parsing code runs in the CLI and in the control plane, from one module — so a manifest that pushes is a manifest that runs. An unknown protocol or verifier kind is refused at push time, not discovered at rollout, because an environment that validates and then cannot run has already cost somebody a GPU allocation.

Check it worked

Prints the protocol, the verifier, the tools, and whether a sandbox is required. The console’s Environments page shows the same, plus which runs have referenced it.

Next

Verifiers for the three ways completion is decided · Writing rubrics · Using an environment

Quality evidence

The user-owned runner common/envkit/quality.py in tuneplane-examples executes your callbacks, then reports through Reporter.environment_quality(). It belongs in your project, not in the control plane. There is no universal model/scorer implementation: connect the same verifier and harness used by your environment, pin a checkpoint, and disable policy updates during measurement. Create an adapter factory such as my_quality:adapter returning keyword arguments for measure(): Use verifier_fixtures(verify, fixture_task, correct=..., incorrect=...) to execute positive, negative and verifier-execution fixtures against your real verifier. The negative fixture should report fail when the known bad answer is rejected. For sandbox-required environments, add egress_allowed, egress_denied and reference_isolation callbacks that actually probe your runtime. Absence is never treated as success. Callbacks must set their own network/process timeouts. A generation callback can return a string or Prediction(text=..., tokens=...); missing token usage stays unknown. Callback exceptions become infra_error, not model failures. The runner records all planned task trials, saves a complete JSON report and a separate local fixture-evidence file, then uploads metadata. Raw fixture evidence stays local; protect that file because it may contain sensitive diagnostics. An upload failure leaves the report available locally and fails explicitly. No model response or reference is included in the ingest report. Inside the owner’s job, with the environment version pinned:
Use a job whose assigned environment split matches the measurement: a training operation for training suitability, an evaluation operation for held-out measurement. Do not override TUNEPLANE_JOB_ENVIRONMENT_SPLIT. Locally, omit --upload and point --directory at your original authoring directory. This is a callable/CLI integration for an existing project job, not a new one-click model execution service. GPU generation and sandbox enforcement remain the adapter’s runtime responsibilities.