Server software grew up around one assumption: the machine stays awake. People post screenshots of uptime counters in the hundreds of days. Code is written as if that's normal. The process stays up, the connection stays open, the clock ticks along one second at a time and nothing ever stops it.
Agents break that assumption, because the computer an agent runs on mostly shouldn't be awake.
Watch one work for a while. It sends a prompt off to a model and sits there until the answer comes back. Then it runs the tests and sits there again. Some tool on another company's server gets called, and more sitting. Eventually it wants permission from me, and if I'm in a meeting or asleep, that pause lasts hours. Somewhere in between are a few seconds of reading files or compiling, and that's the only part where the CPU does anything.
A server waiting for requests is awake the whole time, and somebody pays for it. An agent waiting for me has no reason to be. So the newer platforms built for agents flip it around: asleep is normal, and awake is the exception.
What that looks like on a real platform
DigitalOcean's Managed Agents makes this easy to see, because it's right there in the price list. Every session gets its own small virtual machine, and CPU is billed only while it's busy, at $0.044 per vCPU-hour. Time spent waiting on a model or a tool isn't billed as CPU. Their example: 2 vCPUs at 25% average use comes out at $0.060 an hour, against $0.126 if you paid for the whole machine. Pause it, and you're paying for the snapshot on disk and nothing else. They quote about 305 milliseconds to wake it back up.
I didn't measure those, they're DigitalOcean's figures. What matters is what they encourage. If a pause costs almost nothing and waking takes a third of a second, you pause every time nothing is going on. A session ends up frozen, thawed and now and then copied, many times over its life. That's the exact opposite of a server with an uptime counter in the hundreds of days.
Code that assumes it never sleeps
I spent a few weeks this autumn pausing and resuming and forking agent sessions on that platform to see what breaks. The short answer is that almost nothing breaks the way you would expect, and a few things break quietly.
Open connections. A checkpoint on its own left open connections working in my runs. A copy made from that checkpoint is different. It has the same connection with the same address, port and sequence numbers as the original, and as far as I could tell something in the platform's network refuses to carry its traffic. The application doesn't get an error. Its read just never returns, until a timeout gives up eight seconds later. Rolling a session back to an earlier checkpoint did exactly the same thing to the original.
Randomness. When a running program is copied, its random number generator is copied too. In Node, every copy and the original went on producing exactly the same "cryptographically secure" random values for the first 16.64 million calls before they drifted apart. The kernel noticed the copy and reseeded itself. The program inside it was never told.
Being told at all. That's the general problem. Nothing tells a process "you were asleep for an hour" or "there are now three of you". The only place I found any mention was a single line in the kernel log. Code written for servers has never needed to ask, because on a server the answer was always no.
The same logic reaches further than I tested. A lock or lease held by a sleeping agent is held by nobody who can renew it. A login token that was valid when the session paused may have expired by the time it resumes. A scheduled job doesn't run while the machine running it is frozen. None of this is new to anyone who has written code for laptops or phones, which also sleep all the time. It's just new to server code, and agents are being written like servers.
What I got wrong
My first run couldn't tell the checkpoint apart from the fork, and the checkpoint looked like the obvious suspect for killing connections. It took a second run with the original as a control to see that the checkpoint left them alone. The fork and the rollback were what killed them.
When I first wrote it up, I said the copies saw no reply and no reset. The packet log later showed a reset coming back within about a millisecond, every time. Linux threw them away because they didn't match the connection. From inside the application that looks identical to silence, which is why I got it wrong.
And I thought of the random numbers as a copying problem. The original session says otherwise. It was never copied, and it still produced the same 16.64 million values as its copies, because every one of them carried on from the same frozen state. A server handing out a thousand tokens a second would take more than four and a half hours to get through that many.
What I'd do differently now
- Treat waking up as an event. After a resume, a fork or a rollback, close every connection that existed before and dial again, instead of waiting for a timeout to tell you.
- Generate anything secret after the copy, in a process started after it, not before.
- Keep the agent's real state outside the machine, in a database or a queue, so that a frozen or copied session can't hold anything important on its own.
- Assume time has passed. Check tokens, leases and schedules when the agent wakes, not when it last did something.
None of that is hard. It just isn't what server code does by default.
Caveats
My tests were a handful of sessions on one platform, one Ubuntu image, Python 3.12 and Node 20, plain HTTP connections and runs of a minute or so. I didn't test TLS connections, long sleeps, or other platforms that snapshot and fork virtual machines in their own way. The pricing and the 305 milliseconds are DigitalOcean's published figures, not something I timed here.
The bigger point doesn't depend on any of those numbers. The machine an agent runs on is going to sleep, wake up and be copied far more often than any server we have written code for. The code should expect it.
Top comments (1)
The point that an agent waiting on a human approval can sit idle for hours changes how you design state. If the machine can be suspended, anything held only in process memory (open connections, in-flight timers, partial tool results) is a liability, so we try to persist the plan and pending approval to durable storage and make every resume step idempotent. Have you found that wake-up latency matters in practice for interactive approvals, or is it mostly fine?