Giving the agent a computer is the easy part
Spinning up a Linux box and letting a model run commands in it takes an afternoon. Everything after that afternoon — the file it edits without reading, the export that never reaches the user, the sandbox nobody cleaned up — is where the months go.

The pitch for sandboxed compute is easy to make and mostly true. Your user says "clean this export and chart revenue by region", the agent pulls the file in, writes some Python, runs it, and hands back a PNG. That is a genuinely different product from a chatbot, and it is not hard to demo.
Here is what we learned building the parts that are not the demo.
What the machine is
A microVM per conversation. Its own kernel, its own disk, nothing shared with another customer or another end-user. The agent is root on it, with a working sudo. 2 vCPU, 2 GiB, 8 GiB disk, Ubuntu 24.04, /workspace as the working directory.
It comes with the things this kind of work always needs: pandas, numpy, matplotlib, openpyxl on the Python side; pandoc, LibreOffice, poppler, qpdf, ghostscript, tesseract and typst for documents; ffmpeg, ImageMagick, libvips for media; ripgrep, jq, sqlite3 and the usual shell tooling. Anything missing, the agent installs itself — sudo apt-get install -y works, and the change stays in that conversation's workspace.
Isolation is a virtual machine with its own kernel, not a shared container. No host mount, no Docker socket, and neither your provider keys nor the JWT secret are ever placed inside it.
The boundary that took thought: a sandbox can reach the public internet — fetch a page, install from any registry, call an API you point it at — and cannot reach anything private. That is enforced on addresses rather than hostnames, so there is no allowlist to keep current.
It also cannot resolve them. The sandbox's resolver asks public DNS and never the deployment's own, because otherwise a sandbox could look up internal service names and read the topology off the answers — which the address filter would stop it reaching, having already told it what was there. IPv6 is dropped outright; a VM here is IPv4-only by construction, and an unfiltered v6 packet is traffic no policy was written for.
It reads before it writes
This is the guard I would put into any agent that touches files, and it is worth more than it looks.
Both writers refuse a file the agent has not read. computer_edit_file refuses a file it has never seen. computer_write_file refuses an existing file it has not seen in full — because that one replaces everything, including whatever it did not look at. And both refuse again if the file changed since it was read, because a command it ran, a background job, or your own user may have written to it in between.
"Has not read" means in the current turn, deliberately. The guarantee is not that somebody looked at the file once. It is that the file's current contents are in front of the model as it decides — and across turns they may have scrolled out of the window or been compacted away. So the agent re-reads before editing on a later turn. That costs one tool call.
One tool call is the entire price of not having a confident edit quietly delete a paragraph nobody knew was in the file. I have watched that happen. The diff looks plausible, the agent reports success, and the missing paragraph is found by a person three weeks later.
The silent failure we shipped and had to fix
Producing a file and sending one are two steps:
computer_fetch_document bookings.csv # a corpus document, into the sandbox
computer_bash python plot.py # matplotlib → out/chart.png
computer_view_file out/chart.png # look at it before claiming it's done
computer_export_file out/chart.png # into the conversation
send_file <id from export> # ...and now the user has itLeave off that last line and you get the worst kind of bug. The export succeeds. The file really is there. Nothing errors anywhere. And attachments comes back empty, so from the user's side the agent said "here is your chart" and attached nothing.
We kept the separation anyway, because staging and delivering are genuinely different acts and collapsing them would mean every intermediate artifact lands in the user's chat. But it is the thing to check first when a file does not arrive.
computer_view_file in that sequence is the one people underrate. After rendering a chart the agent can open the PNG, see it, and fix the axis labels before showing anyone — rather than describing a file it never looked at. Charts generated by a model that cannot see its own output are wrong in a specific, recognisable way, and one look fixes most of it.
Totals need the whole file
rag_search returns the passages that matched. A ranked fragment, never the whole document.
So anything depending on all of a file — a total, a count, a sum, a maximum, any per-row arithmetic — has to fetch the file whole and compute over it. The agent is told this, and the retrieval results say it too, because the failure when it goes wrong is silent in the way that matters: arithmetic over a fragment produces an exact-looking number that is simply short, with a row count and a filename attached that make it read as verified.
If you have a domain where this matters, a skill naming the procedure is the strongest guarantee available — a written-down process the agent loads when it recognises the situation, rather than a paragraph in a system prompt that is competing with forty other paragraphs.
And when the file is large: aggregate in SQL rather than pulling raw rows. Only the result preview passes through the agent, so a group by beats fetching 10,000 rows to sum them in Python.
Long commands, and paying for idle machines
Two things make a five-minute command bearable, and both are automatic.
Output arrives on the stream as it happens, grouped by command_id. Append them to a pane and a test run looks like a test run instead of a hang. Really long work runs in the background: the agent starts a build and keeps working, and when it finishes the agent is told and carries on. If the turn had already ended, the result arrives as a new turn in the same session — exactly like a scheduled run, so it reaches the user through whatever you already do with those. Nothing to poll.
The idle behaviour is the part I am quietly pleased with, because it is the thing that makes per-conversation VMs affordable at all:
| idle for | what happens | what coming back costs |
|---|---|---|
| 1 minute | it sleeps, memory written to disk | nothing you can feel — it wakes exactly where it was, processes and all |
| 5 minutes | memory dropped, files kept | a boot, about a second |
| 10 minutes | disk moves to object storage | a few seconds |
| 7 days | deleted | a fresh workspace |
A sandbox running a command is never put to sleep, however long the command takes. And if one has genuinely gone, the next tool call recreates it rather than failing the turn.
One thing to keep in mind
Sandbox output is untrusted input. A file the agent downloads could contain text aimed at the model. It comes back as a tool result rather than as instructions, and guardrails still screen the final answer — but treat sandbox-derived content the way you would treat user-supplied content anywhere else in your product.
Which is the same lesson as everything else here: the machine was the easy part.
Free while we launch, bring your own provider keys. oberik.com
