You want your agent to read a log file inside its container. Not write it, not run it. Read it. So you open the deployment class and add a method. Then the runtime class that implements the method. Then the message that carries it across, the handler that receives it on the far side, the fake used in tests, the config that lists what the agent may do. Eight files, give or take, and the agent can read a log. On the machine where that file actually lives, the operation you spent the afternoon wrapping was one line: open the path, read it, hand back the text.

Work through the gloves or step inside

Picture the sealed box in a laboratory, the kind with heavy gloves built into its front. The hazardous material stays in there. You stay out here, hands pushed into the gloves, and every motion you make is shaped by what the opening allows. Want a new instrument in the work? It has to fit through the transfer hatch, and if it doesn’t, you don’t get to use it.

The other arrangement is to make the room itself the enclosure. You suit up at the door, walk in, and pick up ordinary bench tools with your own hands. Nothing became safe. The barrier moved: instead of running between your hands and every single object, it now runs once, around the room, and everything inside is plain work done plainly.

That is the whole choice. An agent outside the sandbox is working through the gloves. Its reasoning runs on the host, and each action descends a chain — a deployment layer, then a runtime layer, then a protocol that carries the command into the container and the result back out. An agent inside the sandbox runs as a process in the same container as the code it is touching. Reading a file is a file read. Running a command is a subprocess. There is no wrapper left to extend, because between the decision and the effect there is only the operating system.

The barrier moves, it does not vanish

What you gain is not only the eight files. Every call stops paying for a round trip, and when something breaks you get the real error from the real system instead of a transport failure two layers away from the cause. New capability means importing a library that already exists on that machine.

What you give up is the agent’s separation from the thing it can damage. Its own scratch space, its own notes, its own process now sit inside the blast radius of the command it just decided to run. You also give up swapping the ground underneath: teams that move between local containers and different cloud runners on demand still need the abstraction you just deleted, because that abstraction was the part doing the swapping.

The compensation is that review gets a single address. Mediating each call invites you to inspect each call, forever, on every capability you add. One enclosure asks one question instead: what can this container reach — which network, which credentials, which mounted directories. That question is cheaper to answer and much less forgiving when you answer it wrong.

Inside the room, ordinary tools are enough

The simplification carries into what the agent is handed. Once it is standing in the environment, the interface can be the environment’s own. Mount the services it needs as directories — object storage here, a chat workspace there — and the agent reads, searches, copies and pipes with the same handful of verbs on every one of them. The pipeline that counts lines in stored JSON is the pipeline that finds messages in a channel, because both were made readable through commands the model already knows. That is fewer names to tell apart and less weight carried on every turn.

The trap is believing the sameness goes all the way down. Identical paths do not mean identical permissions, and a shared verb can quietly hide a delete that is instant on one backend and slow to settle on another. Keep the common vocabulary, keep the policies separate.

Draw the barrier once, in a place you can actually watch, and the tools stop being the hard part.