Skip to main content
The four tool surfaces (computer, bash, edit, status) are designed so that a {tool_name, tool_input} dict from any tool-calling model maps cleanly to one VM call. This page shows a provider-neutral dispatch function, a generic loop, and a cookbook of useful patterns.

Dispatch — model tool call → VM call

For async, change the function to async def and await env.sdk.computer/bash/edit(...).

Loop — screenshot, decide, dispatch, repeat

init_messages, call_model, extract_tool_calls, append_tool_result are whatever your model SDK provides — Anthropic, OpenAI, your own. dispatch_tool is the only piece that talks to the Plato VM. For the surrounding session lifecycle (testcase → reset → login → evaluate), see Core SDK → Examples → Full evaluation. Drop the agent loop above into the “run your agent” step.

Cookbook

Short recipes that come up repeatedly. Each one assumes desktop = session.desktop_env.

1. Save a screenshot to disk

2. Open a terminal via the GUI and run a command

3. Pre-seed state with bash, then verify with bash

4. Copy a file from the VM back to your host

There’s no dedicated file-transfer primitive — bash + base64 is the idiom. Same three lines work for screenshots, PDFs, logs, CSVs, binaries.

5. Copy a file from your host into the VM

For small/text files, edit is cleanest:
For larger or binary blobs, base64-encode on your side and decode in bash:

6. Drive the VM’s Chrome over CDP from your laptop

7. Install extra tooling on the VM

Most evaluations don’t need new packages on the VM, but occasionally you want a screen recorder, trace collector, or CLI to shell out to during scoring. The pattern is the same regardless of the tool:
The Acquire::Check-Valid-Until=false flags work around stale system clocks on freshly-booted VMs, where apt-get update would otherwise reject the Release file.

8. Record a video of the session with ffmpeg

Start ffmpeg in the background, do whatever you want captured, then stop cleanly. Always use SIGINT (pkill -INT) to stop ffmpeg — only SIGINT lets it flush the MP4 MOOV atom; a SIGKILL’d recording is unplayable.

Pitfalls

  • get_liveview_url() is sync; don’t await it even on the async client.
  • No dedicated file-transfer primitive — use edit(create) for text and bash + base64 for binaries.
  • Stop background ffmpeg with SIGINT, never SIGKILL.