Skip to main content
Read this once before writing much code. The other SDK pages show how to call things — this one explains what they are.

Sims, artifacts, environments

A sim (simulator) is a pre-built VM image of an application — a CRM, a Git server, a wiki, a Linux desktop. Each sim has a name (e.g. espocrm, ubuntu-vm), one or more artifacts (each artifact is a version of the sim), and an is_desktop flag. An environment (“env”) is a single running VM. One artifact → one env. You declare envs with the Env helper:
Env.simulator(name) boots whatever artifact is configured as the sim’s example version. Not every sim has one set, and the example can change underneath you. For reproducible runs, grab an artifact ID from the sim’s Versions list in the Plato dashboard and use Env.artifact(...).

Sessions

A session holds one or more envs. It exists so envs can be reset, evaluated, and torn down as a unit — and so they can reach each other on a private WireGuard mesh at {alias}.plato.internal.
The SDK keeps the session alive in the background. Always session.close() when you’re done.

Computer use envs

A computer use env (a.k.a. desktop VM) is any sim with is_desktop=True — currently ubuntu-vm. Instead of a webapp the agent reaches over a public URL, the agent operates the VM itself: pixels, keyboard, shell, files. Same session and environment APIs as everything else; the only difference is env.sdk, which on a desktop env exposes status / computer / bash / edit / login. See Computer Use.

The lifecycle

Every Plato run follows the same five steps:
  1. session.reset() — initializes mutation logging on each sim. Everything that happens after this gets captured. (Sessions created from testcase= are auto-reset.)
  2. Logindesktop.sdk.login(session) for desktop sessions, session.login(browser) for app-sim sessions. Login mutations are captured by the stream.
  3. Run the agent — drive the env however the task requires.
  4. session.get_state()(optional) peek at mutations mid-run.
  5. session.evaluate() — score the session against its testcase.
For a working example: Core SDK → Examples.

Mutations

A mutation is a tracked change to the env (a database write, a file change) since the last reset(). The mutation stream is what evaluate() reads when scoring MUTATION-style testcases. session.get_state() flushes pending writes and returns the current mutation set, keyed by job id. You don’t usually need to call this directly — evaluate() calls it internally.

Testcases and scoring

A testcase is a pre-defined task: prompt, target sim(s), starting artifacts, scoring config. They live in your Plato tenant with public IDs like "tc_abc123". Starting a session from a testcase is the only path to scoring. plato.sessions.create(testcase="tc_abc123") provisions the right envs, links the scoring config, and auto-resets — session.evaluate() then just works. Sessions created from envs= are for free-form work and cannot be evaluated. session.evaluate() scores the session against its testcase. Two scoring modes; a testcase can use one or both:
  • MUTATION — the testcase declares the database/file changes a successful run must produce. evaluate() reads the env’s mutation stream and checks them.
  • OUTPUT — the agent must return a JSON object matching a schema declared on the testcase. Pass it as evaluate(value=...).

Public URLs vs liveview

Two different “open this in a browser” affordances:
  • session.get_public_url() — for app sims. Returns dict[alias, url] mapping each env to a browser-accessible URL. The agent reaches the app this way.
  • desktop.sdk.get_liveview_url() — for desktop VMs. Returns a noVNC URL you open to watch the VM in real time. Useful for debugging an agent loop.

Sync vs async

Every API has a sync (Plato) and async (AsyncPlato) form — same names, just await the calls. One exception: desktop.sdk.get_liveview_url() is always sync.

Glossary