Building agentsChapter 1 of 5

Two places to run an agent

The short answer

Run the agent loop outside the sandbox, and send only the tool calls into one. The loop is code your team wrote, so many sessions can share a host, and each tool call needs isolated compute only for as long as it runs. Most loops stay inside because of files, so the design works only when the loop and every tool call see the same storage.

Hunter LeathFounder & CEO, Archil

9 min readChecked November 2026Read as Markdown

Teams that build agents well run the agent loop outside the sandbox and send only the tool calls into one. The reason starts with what an agent is. Every agent is a loop. The harness sends the conversation and a list of tools to a model. The model answers with a tool call: run npm test, read src/app.ts, change a line. The harness runs the call, adds the output to the conversation, and asks again. It stops when the model answers without a tool call.

That gives an agent three parts: an inference provider, a harness, and a set of tools. The model is someone else’s API. The other two run on your infrastructure, and they’re very different kinds of code.

· The agent loopThe harness and the model trade messages until the job is done. Every tool call runs a command the model wrote a moment ago.

The agent loop has three parts. The harness sends the conversation and its list of tools to the model, which is an inference provider. The model answers with a tool call, bash: npm test. The harness runs it in the tools and gets back the exit code and output, then sends those to the model, which writes its next step. The loop repeats until the model answers without a tool call. The harness is your own reviewed code, and many loops can share one process. The tools run the model’s commands, like eval, and every call needs isolation.

The harness is code your team wrote and reviewed, like the rest of your product. It can’t decide to misbehave, so it’s safe to multi-tenant: one process can run many users’ loops, the way one web server handles many users’ requests. The tools are the opposite. A tool call runs whatever the model wrote, which makes it closer to JavaScript’s eval than to an API call. Nobody reviewed it, and each call needs isolation from the host and from every other tenant.This split, and most of this chapter’s argument, comes from Hunter Leath’s What comes after sandboxes, published October 14, 2026, after a year of working “closely with nearly all of the frontier companies building in the AI application layer.”

So the tools have to run in a sandbox. The open question is where the harness runs.

Two places to put the loop§

Most teams put the harness in the sandbox too. A user starts a session, you start a sandbox, and the harness and its tools run there together until the session ends. The practice this guide recommends runs the harness on a host that many sessions share, and sends each tool call somewhere isolated: a serverless tool call, which starts compute such as a microVM for one command and stops it when the command returns.

· One session, two designsThe same session under each design. The gray bar is compute that’s held, and each solid mark is a tool call keeping it busy.

One agent session under two designs, side by side, as an illustration rather than a measurement. The session has four short tool calls (git status, npm test, a sed edit, and npm test again) between model turns, and two waits on the person. In the sandbox, the harness and its tools share one sandbox per session. The sandbox is held from the first message to the last and is busy only during the four tool calls. Outside the sandbox, the harness is one of many loops on a shared host. Each tool call runs in its own microVM, which exists only while the call runs, so the CPU held for this session is the four tool calls plus a slice of the shared host. Between calls, no microVM is running.

Both designs do the same work. They differ in what they hold while the agent waits. Inside, every session holds a whole machine, sized for its heaviest step. Outside, the harness is an ordinary service that you deploy, scale, and monitor like the rest of your backend.

Why the loop ends up in the sandbox§

Teams have two good reasons to keep the loop inside. The first is operational. A sandbox per session is one piece of compute to manage. Splitting the agent means two services to run, scale, and debug instead of one.

The second reason is subtler, and it usually decides the design. The harness has file tools too. Suppose the agent’s bash tool runs echo hello > hello.txt in the sandbox, and the model then asks the harness’s read tool for hello.txt. If the harness runs somewhere else, it has its own files, and hello.txt isn’t among them. Every file a tool writes has to reach the harness, and every file the harness writes has to reach the next tool call. An npm install writes thousands of files that the harness’s search tool should see. An edit has to land before the test run that checks it.

Teams that move the loop out end up writing that synchronization themselves. They copy files back after each call, push edits before the next one, and decide what happens when both sides changed the same file. With the harness in the sandbox, the problem disappears, because there’s only one copy of the files.

Where the loop runs, and where the files live
  1. 01 · Toolecho hello > hello.txtworks: Written to the sandbox’s disk.
  2. 02 · Harnessread("hello.txt")fails: No such file. The harness host has its own files, and nothing copied this one over.
  3. 03 · Harnessedit("notes.md")works: Written to the harness host only.
  4. 04 · Toolcat notes.mdfails: No such file. The sandbox never received the edit.

Loop outside, files in a sandbox: two of the four steps can’t find their file. Between calls: a sandbox stays up for the session, and you write the code that copies files each way.

· Where the files areThe same four steps under each design. Pick one to see which steps find their file, and what has to stay up between calls.

The same four steps under three designs. Step 1, a tool runs echo hello > hello.txt. Step 2, the harness’s read tool reads hello.txt. Step 3, the harness’s edit tool writes notes.md. Step 4, the next tool call runs cat notes.md. With the loop in the sandbox, all four steps work, because both sides use the sandbox’s disk, and one sandbox stays up for the whole session. With the loop outside and the files in a sandbox, steps 1 and 3 work, but steps 2 and 4 fail with no such file: the harness host and the sandbox each have their own files, a sandbox still stays up for the session, and you write the code that copies files each way. With the loop outside and one shared disk, all four steps work: the tool’s write is committed to the disk before the call returns, the harness reads and writes the same disk, and the next microVM sees the same file. Between calls, nothing runs for the agent except its loop on the shared host.

What the sandbox does all session§

A sandbox that holds the loop has to stay up as long as the loop does. Most of that time, the loop is waiting on the model or on the person reading its output.

We measured this on our own engineering team’s coding agents. Across a month of agent loops and about 37,000 tool calls, the median tool call finished in 0.5 s. Suppose each loop had started a sandbox at its first tool call and stopped it after 15 minutes without one. That sandbox would have sat idle for 65% of the time it ran.The method is in Do AI agents need a sandbox running for the whole session? We read only when each model turn and tool call started and ended, not what they contained. The work outlives the machine follows the same data through a day. A shorter timer idles less, but it stops the sandbox between bursts of work, and the next call waits for a new one.

Idle time is only part of the cost. You also size the sandbox for the heaviest step the agent might take, such as a build. It holds that size through every wait. The same article estimates that, “depending on the kind of agent session and the kind of work being done,” agents use “100x to 1000x more CPU resources than they actually need.”From What comes after sandboxes. This is an estimate, not a measurement. What it saves does the arithmetic for your own fleet.

Moving the loop out doesn’t fix this by itself. Suppose the harness runs outside but sends its tools to a sandbox per session. That sandbox can start at the first tool call instead of the first message. From then on, it’s held at the same size for the rest of the session.

Bar height: pauses of that length, log scale. pause shorter than the timer: the sandbox stays up, idle longer: it stops, and the next call starts a new one

48%of the time the sandbox runs, it’s idle (75% in the median loop)

337 hof sandbox time for 174 h of tool work

2.8starts per loop on average: 669 of 31,422 pauses outlast the timer

30 days of our team’s agent loops. Idle share and hours: all 384 loops. Pauses and starts: the 363 loops whose timestamps we still had when we re-ran the analysis for this chart.

· Drag the auto-stop timerMost pauses between tool calls last seconds and keep a sandbox up. The few long ones stop it, and they hold most of the waiting.

An explorable chart of 31,422 pauses between tool calls, from 363 of our agent loops, on log scales from 0.1 s to about 3 weeks. Most pauses last 1 to 10 s, while the model writes its next step. A long, thin tail runs through minutes and hours to pauses of days. Pauses of 10 minutes or more are 1.5% of all pauses but hold 98% of the waiting time. Dragging the auto-stop timer splits the pauses: shorter ones keep the sandbox up and idle, and longer ones stop it. Over all 384 loops, a 1-minute timer leaves the sandbox idle 28% of the time it runs (54% in the median loop), for 243 hours of sandbox time against 174 hours of tool work, and 1,303 pauses outlast it, about 4.6 starts per loop. A 5-minute timer: idle 48% (75% in the median loop), 337 hours, 669 pauses, 2.8 starts per loop. A 15-minute timer: idle 65% (88%), 493 hours, 384 pauses, 2.1 starts per loop. A 1-hour timer: idle 83% (96%), 1,019 hours, 226 pauses, 1.6 starts per loop. Pause and start counts come from the 363 loops.

Storage that both sides see§

The sync problem exists because the harness and the tools see different files. Put the files on storage that both can reach, and it goes away. That storage has to make a tool call’s writes visible to the harness and the next call by the time the call returns.

Other platforms pair short-lived compute with shared files too. A Lambda function can mount Amazon EFS or S3 Files to a local directory. A Modal Volume attaches to both Modal Functions and Modal Sandboxes, and a running container sees another’s changes only after they’re committed and it reloads the volume.From AWS’s file system access for Lambda and Modal’s Volumes guide. The obvious ways to build it tests shared designs such as NFS and JuiceFS on an agent’s workload.

Here’s how it looks with Archil. An Archil disk is a file system that the harness and every tool call can reach at once. A serverless tool call, disk.exec(), runs one command in a microVM with the disk mounted as its working directory. The call commits the command’s writes to the disk before it returns. The harness reads and writes the same disk from wherever it runs, and the disk’s object API returns every committed write.From Serverless execution and the S3-compatible API, which is strongly read-after-write consistent with committed writes. Which interface the harness’s own file tools should use is a choice we make carefully in Many agents, one machine. With the tool and the harness on different machines, the hello.txt example becomes:

TypeScript
import * as archil from "disk";
 
const disk = await archil.getDisk(process.env.ARCHIL_DISK_ID!);
 
// The bash tool: one command, in a microVM, with the disk as its working directory.
await disk.exec("echo hello > hello.txt");
 
// The harness's read tool, on the harness host, reads the same file.
const text = new TextDecoder().decode(await disk.getObject("hello.txt"));

There’s no second copy to keep in sync, so the loop is free to leave the sandbox. Once the files don’t live on a machine, no machine has to stay up to hold them. The harness holds only its share of a host that runs many loops.

· One host, one disk per tenantEach tool call borrows a microVM for one command, and the harness reads what the call wrote from the same disk.

One harness host runs the loops for tenants A, B, and C, and each tenant has its own disk. Tenant A’s loop sends echo hello > hello.txt as a tool call. A microVM starts with disk A mounted, writes hello.txt to the disk, and stops. The harness then reads hello.txt from the same disk, with no copy in between. Tenants B and C run npm test and python report.py at the same time, each in its own microVM on its own disk, writing coverage/ and report.csv. Between calls, no microVM is running, and each tenant’s files stay on its disk.

This design doesn’t fit every agent. An agent built to edit itself, changing its own harness or environment, still belongs in a sandbox, the exception that What comes after sandboxes makes. So does a process that has to outlive a single command, such as a dev server or a file watcher. For that step, a long-lived sandbox can mount the same storage, as an Archil persistent sandbox does, so the rest of the agent stays outside.

The rest of this guide builds the outside design, starting with the call that replaces the sandbox. Serverless tool calls covers what that call has to provide, and how Archil’s provides it.