Quick path
In this article
Quick read: what changed, why it matters, and what to do next.
It's 1:17 a.m. The agent is forty seconds into a release build. You close the lid out of habit, slide the laptop into your bag, and start walking to the car before the thought catches up with you: it's still running.
Now you have a small problem with no good answer. If the Mac sleeps the way it normally does when the lid shuts, the build dies somewhere in the middle and you find a half-finished mess in the morning. If you've rigged it to never sleep, it keeps grinding away in a zipped bag with no airflow, fans straining against the lining, getting warmer than anything sealed in fabric should be. One option kills the work. The other heats a closed machine in the dark.
This used to be a non-question, because nothing on your laptop did real work while you weren't sitting at it. That's the part that changed. Local coding agents now keep going after you stand up. The job is no longer just prompt, code, tests. It quietly grew a physical clause: should this machine stay awake, and for how long, and how hot is too hot.
Someone built a small Mac app for exactly this moment. It's worth pulling apart, less for the app than for the idea sitting inside it, which holds up even if you never install the thing.
What Adrafinil blocks, and what it won't
Adrafinil is a macOS menu bar app, MIT licensed, with one job in its own words: "Keep your Mac awake only while AI coding agents are working." It went up on June 5 and had picked up north of a hundred stars by the time I looked, with a Show HN thread running alongside it.
The word doing the work in that description is only. The README is blunt about what it is reacting to. Tools like caffeinate and Amphetamine keep a Mac awake on a switch: you flip it on, the machine stays up, you remember to flip it off, or you don't. Adrafinil ties wakefulness to whether an agent is mid-task and nothing else. No active session, no reason to stay awake.
It learns about those sessions through hooks. The installer wires into a long list of agent tools, including Claude Code, Codex, Cursor, Gemini CLI, Aider, OpenCode, and Cline. When a session starts, it runs something like:
adrafinil acquire <session-key> --tool claude-code --reason "long build"
and when the work finishes:
adrafinil release <session-key>
Two things in there matter more than they look. The hold carries a reason as a first-class field, so the thing keeping your laptop awake can say why. And the holds are reference counted: two agents working at once stack two holds, and the machine doesn't get to sleep until the last one lets go. You can also take a hold by hand with a timer (adrafinil hold --for 30m --reason "deploy"), and agents can request one over MCP.
Here's the part that makes this a physical-world tool and not just a convenience. It blocks sleep with the lid closed. Most "keep awake" tricks only stop idle sleep, the kind that kicks in when you stop typing. Adrafinil also stops clamshell sleep, the kind that fires the moment the lid shuts. The v1.1.2 release on June 22 went out of its way to make the MCP keep_awake tool say this clearly, so an agent asking to stay awake knows it's asking the lid to stay effectively open. That's a meaningfully bigger promise, and the project treats it like one.
A bigger promise needs more ways to back out, and the project carries a few. A thermal cutout. An idle release. Optional process sniffing to confirm a real agent is running and not a ghost. A sound when the lid closes on an active hold, and a summary waiting for you when you open it again. Underneath, the privileged part is kept deliberately tiny: a small root helper that can do exactly one thing, flip sleep blocking on or off, while the actual decisions live in an ordinary unprivileged daemon. Idle sleep uses IOPMAssertion; clamshell sleep uses pmset disablesleep.
The primitive is not the product
You don't strictly need any of this to keep a Mac awake. Someone on Hacker News made exactly that point and reduced the whole idea to a pmset one-liner plus a marker file. They're right. The primitive is right there in the OS, and has been.
But "prevent sleep" is not the thing you actually need at 1:17 a.m. You need the machine to stay awake because a specific job is running, as long as that job is running, unless it gets too hot, and you need it to give the time back the second the job is done and tell you what happened. A pmset flag does none of that. It's a light switch in a house where you want a motion sensor wired to a timer wired to a smoke alarm.
That gap, between the primitive and what the situation demands, is the whole point. The useful unit isn't prevent sleep. It's a lease.
A lease has an owner, a reason, a term, and a way it ends. You don't hold it forever; you hold it for a purpose and you give it back. That framing travels well past one app. Any time an agent can keep a machine doing physical work while no human is watching, you want the wakefulness leased to the task, not handed over for the night.
The line I keep coming back to: don't caffeinate the laptop. Lease wakefulness to the work.
The agent wake lease
If you only take one thing from Adrafinil, take the shape of the lease. These are the fields worth naming in any unattended local-agent setup, whatever tool you use.
Scroll sideways to see all 4 columns.
| Field | The question it answers | Adrafinil's version | What breaks if it's missing |
|---|---|---|---|
| Owner | Who is holding wakefulness right now? | A session key, per agent and task | Nobody knows what's keeping the machine up |
| Reason | Why is this machine awake? | A free-text reason on the hold | An awake laptop with no explanation |
| Scope | Idle sleep, or lid-closed too? | Both, and it says so explicitly | You think it's safe in a bag; it isn't |
| Max duration | What's the longest this can run? | Time-boxed holds (--for 30m) | A hold that outlives its job and never ends |
| Release | What ends the lease? | release, ref-counted to zero | The last agent finishes, the machine stays up |
| Thermal stop | When does heat override the work? | Thermal cutout (e.g. 60°C) | A sealed machine cooks itself to finish a build |
| Receipt | What happened while I was gone? | Lid-close sound, lid-open summary | You learn the result only by guessing |

Read the fields top to bottom and the one-liner falls apart. pmset gives you scope, and only just. Owner, reason, term, release, heat limit, after-action note, all of it is operational work the flag never does. That's the part you're buying, or building yourself.
Where the lease can fail
A lease is only as good as the conditions that end it. Here's where this one gets tested.
Heat is the real adversary. The HN thread went straight there. One commenter who'd used sleep overrides before warned that a laptop in a closed bag can't shed heat, full stop. Another said they'd watched a coworker's laptop keys actually melt. The author's answer was the thermal cutout: set a ceiling, say 60°C, and the machine stops the heavy work before it climbs past it. That's the right instinct, and it's also the field I'd test first and trust last. A thermal stop that's set too high, or that reads the wrong sensor, is the difference between a paused build and a warped chassis. If you run this, prove the cutout actually fires before you ever trust it inside a bag.
Orphaned holds. Every acquire is a promise to release. An agent that crashes, gets killed, or exits dirty can leave a hold standing with no work behind it, your machine awake for a job that ended an hour ago. The idle release and the time-boxed hold exist for exactly this. Treat a max duration as mandatory, not optional. A lease with no term isn't a lease.
Overlapping sessions. Reference counting is the right call, and it's also the thing people misread. Two agents means two holds, and the machine sleeps only when both let go. That's correct, but it means one stuck session quietly keeps the whole machine up no matter how many others finished cleanly. The count protects the work. It doesn't tell you which hold is the straggler. You still want to be able to see what's open.
The privileged helper. Anything that can override sleep policy on a closed laptop is, by definition, powerful. Adrafinil's design answer is to make the root part as small and dumb as possible: one helper that can only toggle sleep blocking, with all the judgment living in an unprivileged daemon. That's a sound boundary, and it's the right thing to look for in any tool that reaches this deep. Small privileged surface, policy kept out of it.
First-run plumbing. Less dramatic, more common. The v1.1.2 notes fixed background service registration on first run. Background agents that need to be alive when no one is logged in are exactly where this kind of setup quietly fails. The lease can be perfectly designed and still never engage because the service didn't come up. Confirm it survives a reboot before you rely on it.
How to pilot this without drama
You don't need to roll this out across a team to learn the lesson. You need one workflow and one careful night.
Pick a single unattended job you already run, or want to: an overnight build, a scheduled local agent, a long test suite, a deploy that runs while you step away. Scheduled desktop agents are the obvious candidate, since they assume the machine is awake and rarely say so out loud.
Write the lease down before you install anything. Owner, reason, scope, max duration, release condition, thermal stop, what the receipt should tell you. Seven lines. If you can't fill in the max duration or the thermal stop, that's not a paperwork gap, that's the risk talking.
Then test the boundaries on purpose, lid open, on a desk, where you can watch. Kill the agent mid-run and confirm the hold releases. Stack two sessions and confirm the machine stays up for both and sleeps after the second. Drop the thermal ceiling low enough that you can actually trigger it, and watch it fire. Only after the stops have proven they work does the closed lid become a reasonable thing to do, and even then, a hard surface beats a bag.
This is the same instinct behind keeping local agent work boxed to a known surface. Agents that act on their own need edges you've checked, not edges you assumed. The wake lease is just that idea pointed at the one resource people forget is a resource: whether the machine is allowed to be awake.
Bring one workflow and map its lease
The change is easy to miss because it's so small. Your laptop used to go inert the second you stood up. Now it can owe a job more time, and something has to decide how much and when to stop. Leaving that decision implicit is how builds die in bags and how laptops cook in them.
If you're running, or about to run, an unattended local-agent workflow, bring us that one workflow and we'll map its wake lease with you: owner, reason, scope, term, release, thermal stop, receipt. You'll leave with a lease you can apply to every other unattended job you run, or we'll wire the whole path as part of process automation. Either way the outcome is the same: a machine that stays awake for a reason, and gives the time back when the reason is gone. If you're sketching the broader guardrails around agents that act on their own, our AI workflow controls cover the same ground at the policy level.
Don't caffeinate the laptop. Lease wakefulness to the work, and make sure the lease knows how to end.
Before the laptop works unattended
Map the wake lease for one agent workflow
Bring one build, deploy, scheduled task, or local-agent run. We will name the owner, reason, scope, max duration, release, thermal stop, and summary before it runs after hours.
Best fit when a local coding agent, scheduled desktop task, or unattended automation needs more time than the operator can watch.
Practical AI Workflow Notes
Want more practical AI operations ideas?
Get short notes on applying AI inside real small-business workflows — from document handling and customer follow-up to internal reporting, compliance, and automation guardrails.
Turn this idea into a pilot
Which workflow should go first?
Use the readiness check to compare impact, effort, risk, owner, and next step before booking a call.
- 3-5 minutes
- Deterministic score
- No sensitive data
Share this post
