wake-on-event — an idle agent that actually sleeps

running in production on our own fleet since May 2026 · MIT · public source release in preparation — this page is the spec

You want a Claude that's always there — ready the instant a message, a webhook, or a teammate needs it. The naive way to get that is a loop that keeps asking "anything yet?" — and a language model in a busy-loop burns tokens around the clock for the privilege of doing nothing. wake-on-event splits those two ideas apart: a dumb, free watcher stays awake so the expensive model doesn't have to.

What is this — a page, a skill, a tool? Like sedev, all three. wake-on-event is the pattern plus the small pieces that implement it: a watcher (cheap shell/Python), a durable cursor, and a Claude Code skill that teaches your Claude the idle discipline — read, act, advance, sleep. This page is the full spec. You can contribute today.
Setting this up with your Claude? This page is written to be read by a model as much as by a human — point Claude Code straight at it ("read this page and set the skill up for me") and it has the full spec, verbatim. No copy-paste, no PDF.

## the model

           cheap, always awake          expensive, mostly asleep
 channel ──▶ watcher ──(only on new msg)──▶ Claude session
              │                                    │
              └── polls a cursor, costs ~nothing   └── wakes, acts, advances cursor, sleeps

## the economics (why this exists)

This is the whole point, so it's worth being blunt about it:

approachidle costlatency to react
Model in a poll-loop ("anything yet?")continuous token burn, 24/7fast
Cron every N minutesone wake per tick, whether or not there's workup to N minutes late
wake-on-event~zero (a shell sleep)instant — wakes on the event itself

Run one agent this way and you save a little. Run a fleet of eight this way — most of them idle most of the time — and it's the difference between a hobby and a bill you can hand a CFO.

## quickstart (what shipping looks like)

One command arms a watcher on a channel and hands it a wake target. The commands below are the shipping CLI — they'll work verbatim on release day:

# install the skill into your Claude Code setup
$ npx wake-on-event init
# arm a durable watcher: poll this channel, wake this session on anything new
$ wake-on-event arm --channel inbox --session my-agent
→ watching. idle cost ≈ 0. next event wakes my-agent instantly.

The watcher re-arms itself across reboots and crashes (see robustness). From then on you don't manage it — you just send things to inbox.

## day-to-day

# nothing is happening — the fleet is asleep, spending nothing
$ wake-on-event status
my-agent   · armed · cursor=1042 · idle 3h12m · 0 tokens since last event

# a message lands on the channel → the session wakes on its own,
# handles it, advances the cursor to 1043, and goes back to sleep.

The discipline the skill teaches the woken Claude is deliberately small: read from the cursor, act, advance the cursor, return to wait. That last step is the one everybody forgets — and the reason naive setups reprocess the same event forever.

## robustness

An "always-on" pattern is only as good as its worst night. The design assumes things fall over:

failureanswer
The session crashes mid-taskThe cursor only advances after the work is done. A crash re-delivers the same event on the next wake — at-least-once, never silently dropped.
The machine rebootsThe watcher re-arms from a startup hook and reads the durable cursor. No lost events, no manual restart.
The watcher itself diesIt's supervised: a heartbeat re-launches it. The expensive part (the model) was never the thing holding the loop open.
A flood of eventsThe watcher coalesces: it wakes the session once and hands it the backlog by cursor range, instead of firing N times.

## what ships

## why not …

… just a cron job?
Cron wakes on a clock, not on reality: it either reacts late (long interval) or wastes wakes (short interval). wake-on-event reacts to the event, at zero idle cost in between.
… a long-running agent that loops?
That's the exact thing this replaces. A model in a loop pays to ask "anything yet?" forever. Move the loop to a shell watcher and the model only ever runs on real work.
… a webhook straight into the model?
You still want the cursor (for crash-safety and dedup) and the release discipline. wake-on-event is happy to be triggered by a webhook — the webhook just becomes another channel the watcher drains.

## get involved

You don't have to wait for the release. There's a way in right now:

Like the project? It's free and MIT. If it saves you a token bill and you want to chip in, ♥ support it — entirely optional.

## roadmap

v0.1Public repo: watcher + startup hook + skill, this spec as README, adapters for bus / file / sedev
v0.2Webhook-queue adapter, coalescing tuning, per-session idle budgets
laterMulti-channel fan-in on one watcher · a tiny status endpoint fleet-status can read directly

## faq

Does it run on a Mac?
Yes — half our fleet is Mac minis. A watcher, a cron/launchd line, the skill. No server needed.
What counts as an "event"?
Anything a few lines of shell can notice: a new row on a bus, a new file, a new sedev drop, a webhook landing in a queue.
What if I want it to also wake on a timer?
Add a clock as one more channel. Event-driven and scheduled aren't opposites here — the watcher just drains whatever wakes it.
Where does this come from?
We run an eight-instance Claude fleet. Most instances are idle most of the time. This is how they stay instantly available without a runaway bill — it's the substrate everything else sits on.

## status

Not a concept — it's the loop this very fleet runs on, today. Left before the public release: lifting our channel adapters out of our internal bus, the startup-hook installer, docs. This page carries the repo link the day it's up.