TL;DR.
Claude Duet, OpenAI Dots, and Meta Muse launched within seven weeks of each other, and they're built the same way: an agent that wakes up on a schedule, reads your mail, calendar, and docs, and decides whether anything needs you. Teams are now building the same pattern on the API for their own staff and customers: inbox watchers, CRM watchers, on-call agents, deal-desk agents. In each of them, most of the cost goes to the check-ins, not the tasks.
- Most check-ins find nothing to do. In our model, a seat on a 3-hour schedule gets 8 check-ins a day, 4 of which see new items and 2 of which need action. A frontier agent pays full price for all 8.
- Checking less often can cost more. Duet checks "every few hours." That's past every prompt-cache TTL, so the agent's 30K-token prefix bills at $4 per million instead of $0.20. On Opus 5.5, checking every 3 hours costs $35.52 per seat-month in our model. Checking every hour costs $25.92.
- Three gates cut it 71%. A cursor diff that costs zero tokens, a Haiku 5.5 triage call that reads only the new items, and Opus 5.5 only when something needs action: $10.17 per seat-month instead of $35.52. At 1,000 seats, that's $304,200 a year in check-in cost, before the agent does a single task.
- With gates, cost follows events instead of the clock. Once the agent only wakes for real events, you can check more often for less. Gated every 15 minutes ($18.13) costs half of ungated every 3 hours ($35.52).
- Below is heartbeat.py, 75 lines:
tick()runs the three gates, fails open when triage is unsure, and audits 5% of "nothing to do" verdicts.price()reproduces every number in this post.
Product facts are from Anthropic's and OpenAI's launch pages and launch coverage, retrieved November 1, 2026. Prices are Anthropic list prices on the same date. The seat model (30K-token prefix, 24K tokens of new items a day, 2 actionable events a day) is ours and illustrative. No figure here comes from Nadir customer data.
Three agents, seven weeks.
| Meta Muse | OpenAI Dots | Claude Duet | |
|---|---|---|---|
| Launched | Early September 2026 | September 29, 2026 (DevDay) | October 21, 2026 (research preview) |
| Model | Not stated in launch coverage | GPT-6 Astra | Built around Claude Opus 5.5 |
| Background mode | Own computer and browser, keeps working | "Proactive research" with read-only tools | Checks connected accounts "every few hours" |
| How background work is billed | Free with a usage limit, from $20 a month after | Included with Pro and up. More Dots or more volume "will cost extra later on" | "Its background checks count toward" your plan limits |
| Acts without asking? | Asks before sensitive actions | Custom Rules: allow, require approval, or ban | Asks before sending, paying, signing, or deleting |
Anthropic's launch page explains why Duet checks every few hours instead of every few minutes. It's to avoid using up plan limits too quickly. Background checks count against the same limits as your chats, and bigger tasks "require more model turns and background work."
That tells you most of what you need to know about the economics. Even the company that sells the model limits how often the agent is allowed to look. If you're building the same pattern on the API, nobody sets that limit for you. Your bill is check-ins per day times the cost of each check-in, times the number of seats.
What one check-in costs.
A background agent doesn't wake up empty. Before it reads a single new email it loads its system prompt, the user's memory, its skills, and the tool definitions for every connected app. We use 30,000 tokens for that prefix. Agents with many connectors carry more.
On a 3-hour schedule, one check-in on Opus 5.5 looks like this:
| Line | Tokens | Rate | Cost |
|---|---|---|---|
| Prefix (system, memory, skills, tool schemas) | 30,000 | $4.00 / M, cache expired | $0.120 |
| New items since last check | 6,000 | $4.00 / M | $0.024 |
| Output (reasoning and verdict) | 800 | $20.00 / M | $0.016 |
| Check-in total | $0.160 |
Most of that $0.160 goes to re-reading a prefix that didn't change. Opus 5.5 cache reads cost $0.20 per million, a twentieth of the input price, so a warm cache would take the prefix line from $0.120 to $0.006. The cache isn't warm, though. Anthropic's default TTL is 5 minutes and the extended one is 1 hour. A check-in every few hours always arrives after the cache has expired.
Caching it anyway makes things worse. A 1-hour cache write costs 2x input, and on a 3-hour schedule nothing reads it before it expires. We covered that trap in The Cache Write Tax. On a heartbeat slower than the TTL, every cache write is wasted.
Checking less often can cost more.
In the gray bars, a frontier agent runs on every tick. The dip and rise between 1 hour and 3 hours is the cache cliff. At one check-in an hour, a 1-hour cache stays warm and each tick pays $0.006 for the prefix. At one check-in every 3 hours, there are a third as many ticks, but each one pays 20x as much for the prefix, so the month costs 37% more.
In the orange bars, the agent goes through three gates first. That line is almost flat, between $10 and $18 at any interval. It barely moves because its cost now follows how many things happened, not how many times the agent looked. Two actionable events a day cost about the same whether you catch them within 15 minutes or within 3 hours.
That changes the product decision. Without gates, a faster schedule costs more, so the agent checks every few hours and is slower to notice what matters. With gates, the faster schedule costs about half of what the ungated 3-hour schedule costs.
Three gates.
Gate 0: ask the source what changed. Most APIs a background agent reads can tell you what changed since your last look, without returning everything. Gmail's users.history.list takes a startHistoryId. Google Calendar's events.list takes a syncToken and returns only changed events. GitHub honors If-None-Match, and a 304 Not Modified doesn't count against your primary rate limit. Slack, Linear, and most CRMs have a cursor or an updated_since filter. If nothing changed, the tick ends here and spends no tokens. In our model that's half of all ticks: nights, weekends, and quiet afternoons.
Gate 1: a small model reads only the new items. The triage call doesn't need the agent's memory, skills, or 40 tool schemas. It needs the new items and a short rubric (does anything here need a reply, a decision, or action within 24 hours?). On Haiku 5.5 that's 7,500 tokens in and 50 out, $0.0008 per call, about 200 times cheaper than an Opus check-in. In our model it handles 4 calls a day for $0.003.
Gate 2: the frontier agent wakes for events, not ticks. Only when triage says "wake" does the full agent load, with its prefix, memory, and tools, and do what Duet or Dots would do. That's 2 real events a day in our model, plus a few audit wakes (below).
Two cheap guards go in front of triage: an allowlist of senders, and keywords that always wake the agent without asking the small model. Your CEO, the on-call alias, and anything containing "outage" don't need a classifier.
What a missed wake costs.
The real risk with this design is a triage model that sleeps through something important. Anthropic's launch page says Duet itself "might misread a thread or miscalibrate what's urgent," and that's with Opus reading everything. A smaller model will misjudge more often. The design has to plan for that.
- Fail open. If triage returns broken JSON, times out, or reports confidence below a threshold, wake the agent. The error should cost $0.16, not a missed contract renewal.
- Audit the sleeps. Send 5% of "nothing to do" verdicts to the agent anyway, and log whether it agreed. That's your miss rate, measured on your traffic. In our model the audit adds 0.1 wakes a day, about $0.016.
- Tune the threshold on the audit, not on a hunch. If the audit finds misses, lower the bar for waking and accept a few more Opus calls. The confidence threshold is the one setting that trades cost for misses, so set it from data.
Gating isn't free. Each check-in adds one small-model call, and on a 3-hour schedule with 6,000 tokens of new items, Haiku adds well under a second. That's fine for a background agent, since nobody is watching it think.
1,000 seats, one year.
Rolling an always-on agent out to a 1,000-person company changes the scale. In our model, a frontier agent checking every 3 hours costs $426,240 a year in check-ins alone, before any of the work people actually asked it to do. Skipping empty ticks takes that to $230,400. Adding triage takes it to $122,040.
These numbers depend on our assumptions. Change the prefix size, the volume of new items, or the event rate, and they move. What doesn't change is the structure: an ungated agent pays for the clock, and a gated one pays for events. Run price() with your own numbers before you budget.
When not to bother.
- Most ticks are actionable. At one check-in every 6 hours with 2 events a day, nearly every tick with new items needs the agent, and gating saves nothing: $11.04 against $11.12 in our model. Gates pay when most ticks are quiet.
- The prefix is tiny. An agent with a 2,000-token prompt and one tool doesn't have a prefix problem. Skip-empty alone gets most of the saving.
- It's push, not poll. If your source sends webhooks, use them. Gate 0 is for the sources that don't.
- Seat-priced products. If you're a Duet or Dots user on a subscription, you don't pay per token. You pay in plan limits, and the vendor sets the schedule. The same logic is why the vendor checks every few hours.
Tutorial: heartbeat.py.
tick() runs one check-in through the three gates. source.changes(cursor) is your adapter: it returns new items and the next cursor (Gmail history ID, Calendar sync token, an ETag, or a timestamp). run_agent() is your existing agent. price() is the cost model behind every chart above.
"""heartbeat.py: wake the expensive model only when a check-in finds work.
tick() runs one check-in: a zero-token cursor diff, then a cheap triage call on
the new items only, then the frontier agent for the few that need it.
price() compares that against calling the frontier model on every tick.
"""
import json
import random
import sys
from openai import OpenAI
TRIAGE_MODEL = "claude-haiku-5-5"
AGENT_MODEL = "claude-opus-5-5"
ALWAYS_WAKE = ("urgent", "outage", "invoice overdue", "legal") # skip triage
VIP = {"ceo@acme.com", "oncall@acme.com"}
AUDIT = 0.05 # share of "sleep" verdicts re-checked by the agent anyway
RUBRIC = """You screen new items for a busy engineer's background agent.
Reply with JSON only: {"wake": true|false, "confidence": 0-1, "why": "..."}.
Wake if an item needs a reply, a decision, or action within 24 hours."""
def tick(source, cursors, client, run_agent):
"""One check-in. source.changes(cursor) -> (items, new_cursor)."""
items, cursors[source.name] = source.changes(cursors.get(source.name))
if not items:
return "empty" # tier 0: no tokens spent
if any(i["from"] in VIP or any(k in i["text"].lower() for k in ALWAYS_WAKE)
for i in items):
return run_agent(items, "rule")
r = client.chat.completions.create(
model=TRIAGE_MODEL, max_tokens=80,
messages=[{"role": "system", "content": RUBRIC},
{"role": "user", "content": json.dumps(items)}])
try:
v = json.loads(r.choices[0].message.content)
wake = v["wake"] or v["confidence"] < 0.7 # unsure means wake
except (ValueError, KeyError, TypeError):
wake = True # broken triage fails open
if wake:
return run_agent(items, "triage")
if random.random() < AUDIT:
return run_agent(items, "audit") # measures missed wakes
return "sleep"
# $ per million tokens: Opus 5.5 in, out, cache read, 1h cache write; Haiku 5.5 in, out
OP_IN, OP_OUT, OP_READ, OP_W1H, HK_IN, HK_OUT = 4, 20, 0.20, 8, 0.10, 0.50
def price(interval_min, prefix=30_000, delta_day=24_000, active_h=12,
out=800, events=2, rubric=1_500):
"""Monthly check-in cost per seat: every tick vs diff vs diff + triage."""
ticks = 24 * 60 / interval_min
live = min(ticks, active_h * 60 / interval_min)
delta = delta_day / live
warm = interval_min <= 60 # 1-hour cache survives
pre = prefix * (OP_READ if warm else OP_IN) / 1e6
write = prefix * OP_W1H / 1e6 if warm else 0
empty = pre + out * OP_OUT / 1e6
full = empty + delta * OP_IN / 1e6
every = (ticks - live) * empty + live * full + write
diff = live * full + write
wakes = min(events, live) + AUDIT * max(live - events, 0)
cold = (prefix + delta) * OP_IN / 1e6 + out * OP_OUT / 1e6
gated = live * ((rubric + delta) * HK_IN + 50 * HK_OUT) / 1e6 + wakes * cold
return [round(x * 30, 2) for x in (every, diff, gated)]
if __name__ == "__main__":
for m in map(int, sys.argv[1:] or [15, 30, 60, 180, 360]):
every, diff, gated = price(m)
print(f"every {m:>3} min frontier every tick ${every:6.2f} "
f"skip empty ${diff:6.2f} diff + triage ${gated:6.2f} "
f"({1 - gated / every:.0%} less)")
python heartbeat.py prints:
every 15 min frontier every tick $ 73.44 skip empty $ 41.76 diff + triage $ 18.13 (75% less)
every 30 min frontier every tick $ 41.76 skip empty $ 25.92 diff + triage $ 13.22 (68% less)
every 60 min frontier every tick $ 25.92 skip empty $ 18.00 diff + triage $ 10.94 (58% less)
every 180 min frontier every tick $ 35.52 skip empty $ 19.20 diff + triage $ 10.17 (71% less)
every 360 min frontier every tick $ 19.20 skip empty $ 11.04 diff + triage $ 11.12 (42% less)
How to adapt it:
- Measure your prefix first. Log
usage.input_tokenson an empty check-in. That number times your check-ins per day is your floor, and it's usually larger than teams expect once every connector's tool schema is loaded. - Write one `changes()` adapter per source. Store the cursor next to the seat. If a source has no cursor, hash the newest 50 item IDs and compare. That still costs zero tokens.
- Keep the rubric short and the input small. Triage sees only the new items, never the agent's memory. Long rubrics bring back the prefix problem in miniature.
- Log every verdict with its reason. "empty", "sleep", "rule", "triage", and "audit" counts per seat per day are your cost dashboard. The audit disagreements are your quality dashboard.
- Run the agent on the cheapest model that handles the event. Not every wake needs Opus. "Reschedule the 3pm" is a small-model task. "Draft the renewal reply with last quarter's numbers" isn't. That's a routing decision, and it's the next gate.
Where Nadir fits.
heartbeat.py decides whether the agent wakes. Nadir decides which model handles each call once it's awake.
Gate 1 and gate 2 through one endpoint. Point the OpenAI client in heartbeat.py at Nadir's OpenAI compatible gateway and set model="auto" on the agent's calls. A trained classifier sends each request to the cheapest model you've allowed that clears your quality floor, so "reschedule the 3pm" and "draft the renewal reply" stop paying the same rate. POST /v1/bucket labels a prompt simple, medium, or complex without a provider call, which is a drop-in signal for the triage threshold.
Cost per seat, per verdict. Every response reports the model that served it and what it cost, so the empty/sleep/wake log in step 4 comes with dollar amounts attached, without a separate billing export.
Measure before you switch. Observe mode serves exactly the model you asked for and records what routing would have chosen, so you can see what gating would save on a week of real check-ins before you turn it on. With your own provider keys, Nadir charges no fee on any plan, and the provider bills you directly. Savings depend on your workload, and nothing in this post forecasts yours.
Start with a free key, or estimate your savings from your current agent spend.
Conclusion.
Always-on agents arrived in September and October 2026 as consumer products, and companies are building the same pattern into internal tools. Running one costs check-ins per day, times the price of each check-in, times seats, and the price of a check-in is mostly the agent re-reading itself. Past the cache TTL, a slower schedule can cost more, not less. Put a zero-token diff and a small-model triage in front of the frontier model, fail open, audit the sleeps, and the agent's cost follows the events instead of the clock. Then check as often as your users need.
Product facts: Anthropic's Claude Duet launch page (October 21, 2026), OpenAI's DevDay coverage of Dots (September 29, 2026), and coverage of Meta Muse's launch, all retrieved November 1, 2026. Prices are Anthropic list prices retrieved November 1, 2026: Opus 5.5 $4 input, $20 output, $0.20 cache read, 2x input for a 1-hour cache write. Haiku 5.5 $0.10 input and $0.50 output, its rate for prompts up to 100K tokens. The seat model is ours, illustrative, and not a measurement: a 30,000-token prefix, 24,000 tokens of new items a day arriving over 12 hours, 800 output tokens per frontier check-in, 2 actionable events a day, a 1,500-token triage rubric, and a 5% audit. Escalations are priced with a cold cache. Task costs after a wake are excluded and are the same in all three designs. Sources: [Anthropic, "Introducing Claude Duet"](https://claude.com/resources/articles/claude-duet). [MacRumors, "Anthropic Launches Claude Duet, a Proactive AI Agent," October 21, 2026](https://www.macrumors.com/2026/10/21/anthropic-claude-duet/). [The Decoder, "OpenAI launches always-on Dots agents to rival Meta's Muse," September 29, 2026](https://the-decoder.com/openai-launches-always-on-dots-agents-to-rival-metas-muse/). [MacRumors, "OpenAI Launches Always-On 'Dots' Agents to Rival Meta's Muse," September 29, 2026](https://www.macrumors.com/2026/09/29/openai-launches-dots/). [CNBC, "OpenAI follows Meta into the red-hot market for personal agents," September 30, 2026](https://www.cnbc.com/2026/09/30/openai-follows-meta-into-the-red-hot-market-for-personal-agents.html). [Anthropic, Pricing](https://claude.com/pricing). [Gmail API, users.history.list](https://developers.google.com/gmail/api/reference/rest/v1/users.history/list). [Google Calendar API, synchronizing resources efficiently](https://developers.google.com/calendar/api/guides/sync). [GitHub, conditional requests](https://docs.github.com/en/rest/using-the-rest-api/best-practices-for-using-the-rest-api#use-conditional-requests-if-appropriate).