← Blog Systems & Memory 6 September 2026 9 min read

Keeping the Mac awake for agents that run all night

Agents now take on work that spans hours. On a laptop, the most common way that work dies is not a bad model. It is the machine going to sleep. Here is every layer Luminair added to stop that, and the bug behind each one.

Fig 01  Five ways an overnight run dies, and what holds eachFrom the git history
WHAT ENDS THE RUN WHAT HOLDS IT SINCE Idle sleep nobody touches the Mac for a while Held automatically while any run is live caffeinate -dims plus an app power blocker, checked every 10 s 29 AUG 2026 Lid closed the laptop goes in a bag pmset -a disablesleep 1, with no password prompt one-time sudoers rule that allows exactly two commands 26 JUL 2026 Flag quietly reset reboot, power change, deep sleep Intent saved, re-asserted on launch, wake, unlock, power events silent only: a wake never pops a password prompt 28 JUL 2026 Work queued while asleep sent from the phone, no pane open Wake drain runs it headless, one item at a time at relay start, then every 10 s, only if nobody else is on it 30 JUL 2026 A job's supervisor dies Keep job reported LOST, not left reading pending forever 6 SEP 2026
Hollow dot: the failure. Filled dot: the layer that now holds it. Dates are the commits that shipped each fix. This post is about macOS; on Windows the awake layers come down to Electron’s power blocker.

The short version

  1. A Mac has two kinds of sleep that matter here: idle sleep, when nobody touches it, and lid sleep, when you close it. They need different fixes, and one of them needs admin rights.
  2. Luminair's Keep awake switch in the menu bar holds both. The first time, it asks for your password once and installs a rule that allows exactly two commands. After that it never asks again.
  3. It remembers that you turned it on, re-arms itself after a reboot or a wake, and reads the real lid state each time you look, instead of trusting a saved value.
  4. While any run is live, the app holds off idle sleep by itself. Work sent from your phone while the Mac slept runs when it wakes, even if no window is open.
02Runs got longer

Hours, not minutes.

A year ago most agent runs were short: ask, wait a minute, read the diff. That is changing. In November 2025 Anthropic's engineering team wrote, in Effective harnesses for long-running agents, that “developers are increasingly asking them to take on complex tasks requiring work that spans hours, or even days.” Their answer was about memory: each new session “begins with no memory of what came before,” so the harness leaves a progress file and git history for the next one to read.

The chart people pass around to show this trend is METR's task-completion time horizon: the length of task, measured by how long a human expert takes, that a model can finish half the time. MIT Technology Review's explainer sums up the trend: “Every seven-ish months, the time horizon doubled.” It also warns that a one-hour horizon “doesn't mean that it can replace one hour of human work in the real world.” The chart measures task difficulty, not how long an agent keeps running.

We are making a smaller, more practical point. Whatever the chart says, people now leave agents working while they sleep, and on a laptop that runs straight into the operating system. A Mac that sleeps stops the processes on it. The agent does not fail loudly. It just stops, and you find out in the morning.

A protection you must remember to arm is a protection that fails.

That line is from a comment in Luminair's own source, and it is the thread through everything below. Each layer was added because the one before it looked on but did not hold.

03The rule nobody installed

“Says on, still sleeps.”

Keeping a Mac awake with the lid shut needs a system flag, set with pmset -a disablesleep 1. The commit that fixed this feature puts the reason plainly: that flag is “the only thing that holds a Mac awake with the lid closed”, because the usual tools “don't survive clamshell”. Setting the flag needs root.

The original code tried the flag silently with sudo -n, which only works if a sudoers rule lets your user run it without a password. It assumed that rule existed. Nothing ever created it. On 26 July 2026 we checked a real Mac: /etc/sudoers.d/ was empty and sudo -n pmset answered “a password is required”. So every toggle fell back to an admin prompt. Cancel it and the flag never changed, while the menu bar showed the switch as on.

The fix makes the first toggle do the one-time setup. A single macOS authorization runs a small helper, keep-awake.sh, as root. It sets the flag first, so the toggle works even if anything after it fails. Then it writes a rule, checks it with visudo -cf, and only installs it if the check passes. A malformed sudoers file can lock you out of sudo, so it is never put in place.

Fig 02  The first toggle, and every one afterSchematic of setSleepDisabled
Switch on menu bar row Power assertion, now no root, no prompt sudo -n pmset … silent path works done,no prompt no rule yet ONE ADMIN PROMPT · KEEP-AWAKE.SH AS ROOT 1 Set the flag first, so this toggle always lands 2 Write the rule to a temp file, mode 0440 3 Check it with visudo -cf; bad syntax stops here 4 Install as root:wheel in /etc/sudoers.d/ Cancel the prompt and the switch still holds idle sleep. Every later toggle is silent.
Drawn from setSleepDisabled and the tray toggle handler in desktop/main.js, and from desktop/keep-awake.sh. The prompt text is “Luminair needs permission to keep this computer awake”.

The rule itself is as narrow as we could make it. It names your login and two exact commands, nothing else:

desktop/keep-awake.shthe whole grant
# Only these two exact commands, nothing else. Least privilege.
printf '%s ALL=(root) NOPASSWD: /usr/bin/pmset -a disablesleep 1, /usr/bin/pmset -a disablesleep 0\n' "$user" > "$tmp"

Four days later, on 30 July, a second look found a deeper gap. The whole feature had been that one flag and nothing else. There was no power assertion anywhere in the app, so when the flag's silent path failed, nothing held the Mac at all. The fix added a first layer that needs no privileges: caffeinate -dims held as a child process, plus Electron's own power blocker, as two independent holds. The comment notes why the -u flag is left out: it declares the user active and turns the display on, and keeping a Mac running should never mean waking its screen.

04Surviving a reboot

Remember the intent, not the flag.

The next report was “the app keeps closing when the laptop is closed”. The mechanism was right, but the flag does not last. According to the fix's own notes, macOS resets it to 0 on every reboot and can drop it on a power-source change. Nothing re-armed it, so the switch said on while the real flag had gone back to 0.

Since 28 July, the switch stores what you asked for, config.keepAwake, separately from what the system currently says. On launch, and whenever macOS fires resume, unlock-screen, on-ac, on-battery or user-did-become-active, the app re-asserts it: start the power assertion first, then check the flag and set it again if it slipped. That re-assert is silent only. A Mac waking up at 3 a.m. never shows a password dialog to nobody.

Two more small rules came out of the same work. On the very first run, the stored intent is seeded from the live flag, so a Mac that was already armed stays armed. And a watchdog checks every minute that the caffeinate child is still alive and restarts it if not, because a cleanup script or a crash can kill it without any power event firing.

05Read it live

Tell the truth now.

The menu bar row has a second line that says whether the lid can be shut. On 5 August it said “lid open” on a Mac that was in fact holding through a closed lid. We checked rather than guessed: pmset reported SleepDisabled 1 while the saved value in config still said false.

Two things were wrong. The saved value was only refreshed on launch, wake, unlock and power changes, so a flag that flipped in between was never noticed. And an older check had tried to confirm the rule with sudo -n pmset -g. That command is not in the two-command grant, so it failed every time, even with the rule installed and working. The check was deleted, with a note in the source not to bring it back.

Now the panel reads the live flag every time it is painted. Reading it needs no privileges, it costs one pmset call, and it only happens while Keep awake is on.

Menu bar panel · Keep awake row
Off
Keep awake
On, lid flag set
Keep awakeNow close your lid, really.
On, lid flag not set (prompt cancelled)
Keep awakeLid must stay open
Redrawn from the row's labels in desktop/tray-panel.html; the real panel's styling differs. The dashed border stands in for the row's warning state. “On” means a power assertion is really held; the second line follows the live SleepDisabled flag.
06Awake by itself

No switch to forget.

All of that still depends on you turning the switch on. The most common way a long task dies is simpler: you leave it running, the Mac idles, and it sleeps mid-run. So on 29 August Luminair started holding the Mac awake on its own whenever work is running.

Every 10 seconds the app checks whether any run is live. If one is and it is not already holding, it starts its own caffeinate -dims and its own power blocker. When everything goes idle, it lets go. These holds are separate from the manual switch's, so neither can release the other's.

There is a limit, and the app says so. This automatic layer needs no admin rights, so it cannot touch the lid flag. The Jobs popover shows a “Working now” list headed “the computer stays awake until these finish”, and under it: “Closing the lid still stops it: arm "Keep awake" in the menu bar for lid-closed runs.”

Why not arm the lid flag automatically too?Because it needs root, and a background process asking for your password whenever an agent starts would be worse than the problem. The sudoers rule only exists after you have said yes to it once, and the automatic layer does not use it.
07Work that waits

When the Mac wakes.

Keeping the Mac up is half the story. The other half is what happens to work that arrived while it was down. You can queue prompts from the Luminair phone app. Until 30 July, the only thing that ran a queue was an open desktop pane polling its own session. Anything queued for a session you did not have open, including every session created on the phone, sat behind a badge.

The fix is a wake drain. It runs once when the relay starts, which is what happens on wake, and then every 10 seconds. It resumes the real session, runs one queued item through the same engine path a phone prompt uses, writes the transcript to disk, and streams the run so the phone can watch. It is careful about what it takes:

SKIP

Anything already owned

A session with an open pane, a run already in flight here, or a queue with auto-send turned off.

WAIT

A queue someone is using

The queue must sit untouched for a minute, judged by watching its stamp change against the Mac's own clock, so clock skew between devices cannot make it barge in. Since 8 August it also asks the presence record whether another Mac is mid-turn in that session.

CLAIM

Before running, not after

The item is claimed with one conditional write that removes it and records it in a shared ledger. If another device got there first, the claim fails and the drain moves on.

Long shell jobs have their own path. Luminair launches them through keep.sh, which detaches them so they survive the session that started them, and records each in a small folder with a status file. The Jobs panel reads those folders. On 6 September we found a gap: a job whose supervisor process had died never wrote a final status, so the panel showed it pending forever. The command-line tool already caught this; the app did not.

Fig 03  How the Jobs panel reads a Keep jobFrom loadKeepJobs
Status file saysSupervisorJobs panel shows
RUNNINGaliverunning
RUNNINGdead, before 6 Seprunning, forever
RUNNINGdead, nowLost (supervisor exited without reporting); LOST written back
DONEn/aExited cleanly (0), with the log tail
FAILED:<code>n/aFailed, with the exit code and log tail
Real labels from desktop/main.js. The supervisor check sends signal 0 to the recorded process id. Writing LOST back to disk lets the finished job age out of the panel like any other.

A job that reads pending forever is its own small version of the problem this post is about. You go to sleep believing work is happening. The honest answer, even when it is bad news, is the useful one.

08Find it in the app

Before you go to bed.

  1. 1Click the Luminair icon in the macOS menu bar and turn on Keep awake. The first time, approve the one admin prompt. If the second line reads Now close your lid, really., the lid flag is set.
  2. 2When a session has jobs, a small count pill sits beside the + in its composer. Click it to open Jobs. While anything runs, the Working now list shows each run with its elapsed time and current tool.
  3. 3Type //doctor. The Mobile relay and queue drain check warns if the wake drain has not run in five minutes, and Keep jobs lists failed jobs with a one-tap clear.

Turning Keep awake off removes the flag and releases the holds. Quitting Luminair releases its power assertion too, so it never keeps your Mac up after the app is gone.

09What we checked

Checked, and not claimed.

2
Commands the sudoers rule allows: disablesleep 1 and disablesleep 0
5
Power events that re-assert Keep awake, plus launch
10 s
Interval of both the automatic work hold and the wake drain
60 s
A queue must sit untouched this long before the drain takes an item

What this post does not claim

  • That a closed lid is safe without the one-time rule. Without it, the switch holds idle sleep only, and the row says “Lid must stay open”.
  • How long a Mac can run lid-closed on battery, or how warm it gets in a bag. We did not measure either.
  • That METR's chart describes how long agents run. As MIT Technology Review points out, it measures task length in human time.

Sources

Leave it running

Turn on Keep awake once, and let the long run finish while you sleep.

Download Luminair →