Product Launch · XiaoHu Explainer

The Mac Sleeps Only After the AI Stops — This Open-Source Tool Nails Keep-Awake

One-click integration with 9 Agents including Claude Code; tasks keep running with the lid closed, and sleep control auto-releases within 50ms of going idle
At a Glance
  • Adrafinil is a macOS menu-bar tool that blocks sleep only while an AI coding Agent (Claude Code, Cursor, etc.) is actively running a task. Once the Agent stops, it instantly restores normal sleep behavior — including lid-closed (clamshell) sleep.
  • Supports 9 mainstream Agents, automatically calling acquire / release through each one's Hook system, with CLI round-trip latency under 50ms.
  • For concurrent Agents it uses reference counting: each session does its own +1 / −1, and keep-awake lifts only when the count hits zero — the Mac sleeps only after the last task ends.
  • Built-in thermal protection: when skin / CPU temperature crosses a threshold, it force-releases every keep-awake assertion to avoid cooking the machine when closed-lid cooling can't cope.
  • The architecture splits into three privilege tiers; root is confined to the smallest Helper component, which exposes just one boolean interface, setSleepBlocked(Bool) — all other logic runs in user space.
1Scenario

You Close the Lid and Sleep — the Agent Keeps Running

Open-source developer kageroumado recently released Adrafinil, a macOS menu-bar tool that blocks sleep only while an AI coding Agent is actively working, then automatically restores normal sleep once the Agent stops.

3 a.m. — you're asleep, the Agent isn't. It's still churning away in the session you started hours ago, and you closed the laptop lid the way you'd close your eyelids — except this eye never truly shut. That's the conflict: the Agent's task cycle and the Mac's sleep policy have no idea what the other is doing. Close the lid, the system sleeps on schedule, and the task snaps off halfway.

Adrafinil binds sleep-prevention precisely to the AI Agent's work state: when the Agent is genuinely running a task it holds out (lid closed included); the moment the task stops, it hands sleep control right back.
The first tool to switch macOS sleep-prevention precisely on the Agent session's activity state. CLI round-trip under 50ms, one-click Hook integration with 9 mainstream Agents, reference counting for concurrent sessions, and a root footprint squeezed down to a single boolean interface.
<50ms
Round-trip latency from the CLI's acquire / release commands to the Daemon — no drag on the Agent's workflow
9
AI Agents supported with one-click Hook install
3
Privilege tiers (App/CLI → Daemon → Helper), minimizing the root surface
1
Root interfaces the Helper exposes: setSleepBlocked(Bool)

The name itself is the design spec. Adrafinil borrows from a eugeroic (a wakefulness-promoting drug). Unlike a stimulant, a eugeroic perks you up only when needed and stays out of the way otherwise — exactly what separates it from "always awake" tools like caffeinate: it keeps awake only when there's work to do.

2Comparison

Why the Old Approaches Fall Short

Before Adrafinil, keeping a Mac awake for a long task left you only two extremes — and neither lined up with the window when the task is actually running.

caffeinate and Amphetamine are the stimulant approach: switch them on and the machine stays awake the whole time, work or no work. The task finishes early and the machine idles on, burning power and heat. The flip side: install nothing, close the lid, and it sleeps right away, cutting the long task off. Below, all three sit on one timeline — whether the keep-awake window aligns with the task window is clear at a glance.

Kick off a long task at night ─────────────▶ task done
Task actually running (baseline)
caffeinate / Amphetamine
Install nothing
adrafinil
Task windowKeep-awake (blocks sleep)Lid closed = sleep, task cut off

caffeinate's keep-awake bar is lit from start to finish — that long stretch after the task ends is pure waste; the "install nothing" row has a red X at the lid-close point, severed before the task is done. Only adrafinil's lit bar hugs the task window exactly, not a second extra on either side.

3Core Mechanism

Breathing in Step with the Agent

Adrafinil's core is simple: when the Agent starts working it requests keep-awake; when it stops, it releases. The Mac stays awake only for the stretch the Agent is actually running a task. The menu-bar icon has just these two states.

awake state: an Agent is working
"awake" state: an Agent is at work, and the menu-bar icon shows keep-awake on. Source: project README
sleeping state: no Agent, normal sleep
"sleeping" state: no Agent running, the Mac sleeps normally. Source: project README

The Agent doesn't talk to Adrafinil directly — it calls Adrafinil's bundled command line through its own Hook system:

adrafinil acquire <session-key> --tool claude-code --reason "long build" # at the start of a turn adrafinil release <session-key> # when the Agent goes idle

Take Claude Code as an example — the full signal chain flows like this:

UserPromptSubmityou send a command
acquirecount +1
Agent workingblocks sleep
Stoptask stops
releasecount −1
count hits 0restore
Mac may sleeplid closed = sleep
Hero · Activity-Scoped Keep-Awake

The key: it holds for the "actively working" stretch, not the "program is open" stretch. Claude Code calls acquire on UserPromptSubmit and release on Stop, so a session that's open but parked at the prompt waiting for you to type lets the Mac sleep normally. This is activity-scoped keep-awake, as opposed to session-scoped.

The direct payoff for developers: you can close the lid and walk away with confidence, letting the Agent run builds, tests, and deploys; the moment the task ends, the Mac restores sleep on its own — no remembering to turn caffeinate off, no worrying you forgot and left it idling forever.

4Multi-Session

Run Several at Once Without the Mess

What about several Agents running together? Adrafinil uses reference counting: +1 when each session starts, −1 when it ends, and keep-awake lifts only when the count reaches zero.

An Analogy

The office lights go off only when the last person leaves — not whenever just anyone heads out. Any single task ending in between never hurts the others still running.

The counter ring below is the centerpiece of this whole page. Try lighting up a few Agents: as long as the number in the ring is greater than 0, the Mac stays awake (blue pulse); switch them off one by one, and the instant the count hits zero, the ring goes gray and the Mac can finally sleep.

Active sessions

Click to start / stop any Agent. The number in the ring = sessions currently holding keep-awake; only zero lifts it.

5Safety Net

Closed Lid, No Cooked Machine

Running long tasks with the lid closed, the real fear is shoving it in a bag where cooling can't keep up and the machine bakes. Adrafinil has built-in thermal protection — the key safety net that makes closing the lid and leaving feel safe.

SMC temp sensingcontinuous monitoring
Skin / CPU tempover threshold
Force releaserelease all assertions
Mac recoverssleeps & cools normally

Beyond temperature, there's a second auto-release: if the process holding an assertion has already died, or the CPU has been idle for more than N minutes, the matching keep-awake is dropped automatically — plugging the "program crashed but keep-awake never withdrew" hole.

With the lid closed the screen is off and can't pop a notification, so it confirms keep-awake took effect with a chime; when you reopen the lid it hands you a summary: what ran while you were away, the peak temperature, and whether thermal protection ever fired.

Process sniffing (optional)

Even without Hooks installed, the Daemon can auto-acquire when it spots a known Agent program running. This is optional — a fallback for cases where configuring Hooks isn't convenient.

6Security Architecture

Root Does Exactly One Thing

Blocking lid-closed sleep can't sidestep root privileges — that's a hard macOS rule. Adrafinil's move is to shrink root's footprint to the bare minimum.

First, why root is unavoidable. The Mac has two kinds of sleep-prevention: blocking "idle sleep" needs only an ordinary API (IOPMAssertion); but blocking "clamshell sleep" (lid-closed) requires the root-level pmset disablesleep. Adrafinil does both, so it can hold up while running tasks with the lid closed.

An Analogy

An ordinary employee can extend the office lights' timer (idle sleep), but unlocking the whole building's access control so no one gets trapped (clamshell sleep) takes admin privileges.

Its fix is to lock that bit of root into the smallest possible box. The whole architecture is three tiers, read top to bottom:

Adrafinil.app user space · menu bar
Status icon, settings, install-wizard GUI, lid-open summary. A pure presentation layer — quitting or restarting it doesn't affect keep-awakes already held.
XPC
AdrafinilDaemon user space · LaunchAgent
Reference-count registry, process monitoring (kqueue NOTE_EXIT + periodic sweeps), thermal monitoring (SMC), lid-close chime, CLI socket. All policy lives here — it's the single source of truth.
XPC · privileged Mach service
AdrafinilHelper root · LaunchDaemon
The only component that touches the sleep-prevention API. Exposes just setSleepBlocked(Bool) plus a read-only status query, and verifies the caller's code signature. Holds no policy logic.
The adrafinil CLI ships with the .app, symlinked into PATH, talks straight to the Daemon socket — round-trip <50ms
Hero · Root Minimized

All policy lives in user space. Reference counting, thermal monitoring, process monitoring, and the CLI socket all run in the unprivileged Daemon; the only root-running Helper exposes a single state-changing interface, setSleepBlocked(Bool), responsible solely for flipping that final switch. Auditing this code? Far less of it to read.

LaunchAgent vs LaunchDaemon, and pmset's side effects

A LaunchAgent starts after the user logs in, running as that user; a LaunchDaemon starts at boot, running as root. Adrafinil deliberately makes the Daemon a LaunchAgent (user privileges) and only the minimal Helper a LaunchDaemon (root), shrinking the privileged surface.

Two more engineering notes: public IOPM assertions (the very set caffeinate uses) simply can't stop clamshell sleep, so v1 went with the blunter pmset disablesleep 1, which disables idle sleep along with it and must be cleared at shutdown or it leaks — so on restart the Helper first resets the state to disablesleep 0 and then re-applies it.

7Support & Install

Which 9 It Supports, and How to Set It Up

One command writes the Hook into every Agent's config. First, the support list:

Claude CodeCodexCursorGemini CLIAiderHermesOpenCodeClinePi

Install It

Download the signed and notarized dmg, open it, drag it into Applications, and launch. The first launch asks once for admin privileges to register that privileged Helper. After that, a single install-hooks command writes the Hook config into all the Agents above; when you're done with it, uninstall-hooks clears out every Hook entry it added.

System Requirements

macOS Tahoe 26.4 or later (earlier 26.x may work, but the author hasn't tested it). Building it yourself needs Xcode 26 or later with Swift 6 strict concurrency enabled. A non-admin install puts the CLI in ~/.local/bin instead of /usr/local/bin.

Two More Commands Worth Knowing

For background tasks that outlive the reply (a single long build or deploy), you can use the timed hold command to hold on for a while and auto-release when time's up; Agents with MCP support can also call the tools provided by adrafinil mcp directly:

adrafinil hold --for 30m --reason "deploy" # auto-releases after keeping awake up to 30 min adrafinil mcp # speaks the MCP protocol over stdio, for Agents
Adrafinil only intervenes when an agent (Claude Code, Codex, Cursor, …) is mid-task, and gets out of the way the moment that work finishes. kageroumado · adrafinil README(GitHub)
Source: this piece is compiled from kageroumado's adrafinil project documentation published on GitHub (the README and Docs/ARCHITECTURE.md). The project is open-sourced under the MIT license. All performance and mechanism descriptions are as stated in the project docs.