Skip to content

codemode-executor

A model writes a program, and something has to run it. Here that is a V8 isolate created for one call, inside a child process the executor supervises, on a machine you run yourself.

Tool calling puts the model in the loop for every step: one call, one round trip, and the result back in its context, read or not. A program collapses that into one execution. Your tools get called from inside it, the rows nobody needs stay where they are, and the model sees only the answer. Cloudflare’s Code Mode makes that trade on the Workers platform.

But nothing reviews that program on the way, and it has to execute somewhere that can contain it. Here that place is a V8 isolate created for the one call, inside a child process the executor supervises. This project ships that isolate, the bounds around it and the boundary the tool calls cross, and a Node process you run is all it needs underneath.

This call carries the program, the tool it may use and the network setting it runs under:

import { IsolateExecutor } from '@supolka/codemode-executor'
const overdue = new Map([
['acme', [120, 340, 830]],
['globex', [55]],
])
const executor = new IsolateExecutor()
const { result, error, logs } = await executor.execute({
code: `async () => {
const amounts = await billing.overdue('acme')
return amounts.reduce((total, amount) => total + amount, 0)
}`,
providers: [{ name: 'billing', tools: ['overdue'] }],
dispatch: async (provider, tool, argsJson) => {
const [account] = JSON.parse(argsJson)
return JSON.stringify({ result: overdue.get(account) })
},
outbound: null,
})
// { result: 1290, error: undefined, logs: [] }
await executor.dispose()

billing is the proxy the executor generated from that declaration, and billing.overdue('acme') sends the provider name, the tool name and the arguments out of the isolate as three strings. dispatch takes them here in your process, next to the data and the credentials the sandbox never holds. Its answer travels back as JSON, the proxy unwraps result, and the program adds the three numbers up without any of them passing through the model.

error is the field to read first. Programs that throw put their message there and leave result undefined. None of that is a failure of your host, and that text is the input to the model’s next attempt. Broken workers and full queues are different, and those throw with a code on the error. That envelope is also all that survives the run, because the isolate starts with an empty global and is disposed as the result comes back.

V8 owns the memory limit, and the engine itself ends a program that exhausts the isolate’s heap. Time is read on the host clock and never from inside the guest, so a program that never yields still settles at its bound. But a caller who never gets a worker is a different problem: overload is a typed fault at the queue bound, and the queue never grows until the machine gives out. And whatever a run sends outward is measured as it is built. Each transport in the path caps a single message, and a run past that cap would lose its connection before its answer arrived.

Every one of those is a safety claim, and this repository treats it as one. The threat model names the boundary behind each bound, says where that boundary stops, and hands the rest to whoever deploys it. An adversarial corpus attacks those boundaries from inside the isolate on every run of pnpm verify. Each case is judged on what the host observed and never on what the program reported. The benchmark states the method behind every number.

Start here if you have not run it yet, and the two pages under it get a first execution out of either arrangement. From there the guides take one host decision per page: what the model may call, whether it reaches the network, which bounds apply to it, how failures come back. Hosts already running @cloudflare/codemode belong at the migration instead. It names the one default that reverses when you swap.

Past that the book stops teaching and starts specifying. The contract states what another implementation has to satisfy, the operator guide covers what a deployment decides, and the internals describe each subsystem from the code that implements it. Changing any of it starts at contributing.