Leviath, an agent runtime: one file, thousands of agents

Leviath

11 min read Original article ↗

One file describes the agent. One binary runs ten thousand of them.

A model, a toolset and a context budget per stage, not per agent. One 39 MB Rust binary, nothing else to install.

10,000+agents in one process64 msfrom nothing to a running agent30 of 30jobs finished on data too big to fitHow these were measured

Install

curl -fsSL https://leviath.dev/install.sh | sh

macOS: one file, 33 MB on disk.

What goes wrong

Long agent runs fall apart in the same five places.

Long runs forget what you told them

Fifty tool calls in, the one constraint you opened with has been squeezed out by file dumps and stack traces.

Context regions

Grepping costs what planning costs

One model, one toolset and one prompt run the whole job, so the reasoning you pay a premium for is also the thing listing your files, and the only thing judging whether a fifth identical edit was progress.

Stages

A crash mid-run means starting over, or doing it twice

The process dies with hours of tool calls inside it, and nothing left on disk can tell you which of them already wrote a file, ran a migration, or pushed a branch.

Run journal

A handful of agents fills the machine

Every run brings its own process, its own memory, and its own copy of the runtime, so the box runs out long before the work does.

One shared world

You can interrupt the agent, but not its sub-agents

Three levels down, a worker hits a question only you can answer and guesses instead, because the only conversation on offer is with the agent on top.

Live steering

One file you write, and two things you never touch

The file is where you say what the agent does: its steps, which model each step gets, when it should hand you the keyboard, when it should stop and rethink, and how much it keeps in mind. The runtime reads that file as a graph, and it is the same graph you watch a real run move through in The Lair, or from your terminal with lev dash.

Leviath ships with 7 agents, so you never have to write one to start. The file below is one of them.

coder/agent.leviath
[stages.plan]                            ← Stagesmode = "interactive_points"              ← Live steeringmodel = { models = [{ provider = "anthropic", model = "claude-sonnet-5" }] }available_tools = ["read_file", "ask_user_choice", "edit_document"]max_iterations = 20[stages.implement]model = { models = [{ provider = "anthropic", model = "claude-opus-5" }] }available_tools = ["write_file", "edit_file", "bash"]max_iterations = 50[stages.implement.transitions.reassess]  ← Stagescondition = "stuck"stuck_after_iterations = 20stuck_after_minutes = 15stuck_after_same_file_edits = 5hint = "No forward progress - step back and reassess"[context.regions]                        ← Context regionstask         = { kind = "pinned",         budget = "2%" }plan         = { kind = "pinned",         budget = "5%" }conversation = { kind = "sliding_window", max_items = 40, budget = "20%" }

Real lines, trimmed for length.

becomes

lev run coder

agent-chosen when stuck automatic▤ parallel workers

All three are The Lair, which is part of this site rather than part of the binary: it runs in your browser and talks to the daemon on your own machine, so no run has to leave your computer to be watched.

The two you will not find in that file are the two nobody should have to set up: every agent lives inside one process, thousands at a time on one machine, and every step is written down as it happens, so a run that gets killed picks up instead of starting over.

Write your own →

Measured

What happens when the window runs out

Same binary, same tools, same permissions, same iteration budget. The only thing that differs is whether the context is one growing window or a pipeline of stages with a verifier at the end.

Runs that produced the report at all

Context window

Single-model loop, hardenedStructured agent

32k0 / 1010 / 1064k0 / 1010 / 10128k0 / 1010 / 10

One report of 17 exact figures, read out of a log corpus larger than the window, at three window sizes. The single-model loops here are the hardened ones: they are told in the prompt to wind down before the window fills and keep their results clean, which is every discipline the pipeline enforces, written as instructions. None of them returned a report. Neither did the ten plain single-loop runs at each size, left off so both bars share a scale. The failure is not a wrong answer but the model going quiet near a full window.

Invented figures in the delivered reports

Ten runs at 128k

Without verificationthe same six stages, minus one8 fabricatedWith verificationthe shipped recommendation0 fabricated

Both bars are the same pipeline on the same task at the same window; the second one has a verify stage that re-reads the corpus for every figure before the report ships. Without it, three of five reports came back complete, well formatted, and filled in from training priors: round-hour timestamps, canonical spellings the corpus deliberately avoids.

The caveats: our own tasks, built to stress a window rather than sample a normal day - read it as the edge, not a general score. Five runs of each, one round, pre-freeze. We built the structured agent and improved it twice while these ran: structural changes, not answers tuned to a task, and the one kind of job we never reworked is one we still lose. The single loop got no equivalent pass, because there is nowhere in one window to put one - the finding rather than a flaw in it. Both agents, the tasks and the runbooks are public: the benchmark harness. The full run tree publishes when the round freezes, whatever it says.

The honest map

Structure is survival gear, not a default

The question is not which agent is better. It is whether the work fits in one window, and Leviath is built to be either answer.

  • Use one loopIt all fits in the windowIf the material, the tools and the conversation fit with room to work, a single loop is the right shape. It is cheaper, it is faster, and in our own measurements it scores at least as well - sometimes better. Reach for nothing more.
  • Use structureThe material is bigger than the windowOnce the corpus cannot fit, the loop is not choosing what to keep - the window is, by age. Stages let you decide what survives each boundary, which is the difference between an agent that runs out of room and one that was given a plan for it.
  • Use structureA wrong answer is worse than noneWhen the deliverable has to be exactly right, put a stage between the draft and the reader whose only job is to check it against the source. A pipeline can require that. A prompt can only ask for it.

Both shapes are the same binary and the same blueprint file. A single loop is one stage with a big window; the pipeline is six with budgets between them. Choosing wrong is an edit, not a migration - which is why we can afford to tell you when not to use the complicated one.

Measured

Ten thousand agents, and the runtime out of the way

These came off one machine, an Apple M3 Max with 16 cores, running a mixed fleet of the bundled agents against a mocked model that answers in a flat 1.5 seconds, from one 39 MB binary.

Memory is live memory rather than RSS, which is the smaller of the two figures: what the process still holds once the pages the kernel can reclaim come out of it. CPU is a share of the whole machine, so 100% would be all sixteen cores.

The mocked model is the point: a real one would bury what the runtime costs under its own latency. So this is the floor the runtime adds rather than a forecast for your workload, and with a real model the first wall you meet is your rate limit rather than your machine.

Peak memory, by how many agents were running

Agents spawned

1034 MB100197 MB1,000945 MB10,0002.8 GB

Inference pool held at 512 for every tier, so the only thing changing is the number of agents. The runtime is paid for once, so the cost per agent falls as you add them: 3.4 MB each at ten, 0.28 MB each at ten thousand. That top tier is where the ladder stops rather than where the machine did: 2.8 GB of 128 GB, with CPU peaking at 44%.

Runs in flight at the same moment

Agents spawned

10131001361,0001,00210,0006,970

Same fixed pool of 512. Agents in this fleet start sub-agents, so more runs happen than were spawned; how many overlap at one instant is a separate number, and at ten thousand it comes out lower.

From cold, with nothing warm anywhere

Scenario

Daemon boot22 msNew run64 msPaused run resumed70 ms

Starting the daemon is measured inside each scenario rather than subtracted from it, so the 64 ms to begin a run already contains the 22 ms to boot one.

CPU while 1,000 agents were running

Inference pool width

1285.2%2565.9%5124.5%1,0243.4%

Averaged across the span where runs were actually active, so idle time at either end cannot flatter it: under 6% of sixteen cores at every pool width.

Time for 1,000 agents to finish, by inference pool width

Same fleet, same machine, one setting changed.

277spool 128

160spool 256

105spool 512

89spool 1,024

Agents spawned, inference pool held at 512

  • 10
  • 100
  • 1k
  • 10k

Memory over the whole run, and after it

Live memory

Seconds since the tier started

Every tier climbs while there is work and drops back to nothing when the last run drains. A leak would be the line that never comes back.

CPU over the same runs

Share of the machine

Seconds since the tier started

Ten thousand agents hold the machine at roughly 40% of sixteen cores for eleven minutes, and the smaller tiers barely mark the axis at all. Most of what is left is time spent waiting on a model.

Inference pool width, 1,000 agents spawned

  • 128
  • 256
  • 512
  • 1,024

A wider pool puts more of the work in flight

Live memory

Seconds since the tier started

The same thousand agents, four pool widths. A wider pool runs more of them at once, so the memory is higher while it lasts and it lasts a lot less time: 128 stays under 400 MB across five minutes, 1,024 peaks past 1.2 GB and is finished in ninety seconds.

CPU across the sweep

Share of the machine

Seconds since the tier started

Note the axis: this one tops out at 20% of the machine, not 100%. Read the area rather than the height: the total CPU spent falls at every step, from 14.5 machine-seconds at 128 to 2.9 at 1,024 for the same thousand agents, mostly because the wider pool is finished sooner.

All three repetitions are drawn in every chart, not an average of them, which is why each colour is three lines almost on top of each other. The bars above report the median of the three, so the tallest line here runs slightly above its bar.

For scale

The closest thing to a comparison

We do not benchmark other frameworks. Whatever baseline we wrote, somebody could fairly say we wrote it to lose, so here is a benchmark someone else ran. It measures a different thing to ours, and both sets of numbers have gaps worth knowing about before you put them side by side.

Somebody else measured these, on a workload that is not ours, with a memory metric that is not ours either. What that leaves is context, not a scoreboard.

  • Different workloads. Theirs sends 50 requests, 10 at a time; the ladder here spawns up to 10,000 agents. Neither answer substitutes for the other.
  • Theirs calls GPT-5.1 over the network, so a good share of what it times is the model. We mock the model at a fixed latency, which isolates the runtime but also means we publish nothing about real end-to-end speed.
  • The memory columns are close relatives rather than the same measurement. Theirs is peak RSS; ours takes out the pages the kernel can reclaim, which makes it the smaller figure. Subtracting one from the other would not mean much.
  • Cold start is the thinnest column on either side. The article reports that these frameworks "initialize" in that time without defining what it covers, and two of them land on the same 4 ms, so there is not much to compare against. Ours measures more (a separate daemon starting and answering a socket) and is correspondingly slower, which is a difference in scope rather than a result.
  • Reproducibility cuts both ways. Theirs does not name its cloud instances or how many times it ran, so a rerun would be hard to line up. Ours names the machine and the binary and publishes every repetition, on exactly one machine.

The Claude Agent SDK and Codex are not here because we could not find published figures for them on this axis. What circulates traces back to posts with no method attached, and a gap seemed better than a citation like that. If you measure them yourself you will have better numbers than anyone currently quoting them, ours included.

Who builds this

Leviath is written by Gerald McAlister, and it is MIT licensed. One person, in the open, with the changelog as the record of it.

Bugs, security reports and questions all go to the issue tracker, which is the only place any of them gets answered in public. Open an issue