Our companies

How do you give an AI agent a code interpreter that keeps state?

Run a Jupyter kernel inside a sandbox and send it one cell at a time. The kernel is a long-lived Python process, so variables, imports and open files stay between calls, the way they do in a notebook. Put the kernel in its own microVM and the model's code can't touch your machine or other users.

Updated November 10, 2026

Why does an agent need state between code calls?

A code interpreter tool lets a model write Python, run it and read the result. If every call starts a fresh process, the model has to reload its data and redefine its functions each time. A kernel keeps the namespace alive, so the model can load a file once and keep working with it, like a person in a notebook. Running that kernel in a disposable machine keeps the code it writes away from anything that matters; see running untrusted code.

What are the pieces?

Sandboxer's guide to building a stateful code interpreter with Jupyter uses four:

PieceWhere it runsWhat it does
ipykernelThe sandbox, started detachedHolds the variables, imports and open files
A connection fileThe sandboxThe kernel's ports and signing key, readable only by the sandbox user
A small client scriptThe sandbox, once per cellSends one cell and prints its output and status
sbx execYour appRuns the client script with the model's code

Jupyter isn't preinstalled; the base image has Python 3.12 and uv, and the guide installs ipykernel and jupyter_client in a venv. The client uses the Jupyter messaging protocol, which returns stdout, results and tracebacks for each cell.

What happens when a cell fails or runs forever?

An error doesn't reset anything: after a ZeroDivisionError, earlier variables are still there. A runaway loop is the harder case. If sbx exec hits its timeout, 30 seconds by default and at most 600, it stops the client but the kernel keeps running the cell. The guide's fix is a per-cell limit inside the client that sends the kernel an interrupt, which stops the cell and keeps the namespace. Keep that per-cell limit below the sbx exec timeout.

Does the state survive a stop and resume?

Usually. Stopping a Sandboxer sandbox saves its memory, and in the guide's test the same kernel process and its variables were still there after a resume. Memory is restored only on a machine with the same CPU type; when it isn't, the resume reports why, and the kernel is gone. Running the kernel as a systemd service brings it back after a cold boot, but with an empty namespace. A stopped sandbox bills nothing for compute and keeps its data for 14 days after it last stopped. See pause and resume.

How do you keep users apart?

Give each user, or each conversation, its own sandbox with its own kernel. Each sandbox is a separate microVM, so one user's code can't see another's variables or files, and traffic between sandboxes is blocked. Keep your own keys out of these sandboxes; see should an AI agent run on a server or in a sandbox.

Frequently asked questions

Do I need Jupyter Server or a notebook UI?

No. ipykernel and jupyter_client are enough when your app reaches the kernel through sbx exec or the API.

Can the agent install packages mid-session?

Yes. Install into the kernel's venv, then import in the next cell; the running kernel picks it up without a restart.

What does an idle interpreter cost?

Nothing for compute while it's stopped. An idle sandbox also pauses itself with its memory after a plan-dependent number of minutes and wakes on the next request.

Is the kernel's traffic exposed?

No. The kernel listens on localhost inside the sandbox, the client runs there too, and messages are signed with a key only the sandbox user can read.