← Paul Barrick

// the agentic os

Headlines

An interactive notification service for LLM agent sessions. Whether three sessions are running or forty, it tells you when one actually needs you, and you approve, deny or steer it right from the card, without switching windows or breaking your train of thought.

v0.1 · running on the Mac

Diagram of the event flow: producers such as Collab, Loop, cron jobs and agents push events through three doors — the CLI, HTTP and an inbox folder — into one envelope; policy decides whether to deliver, and channels fan out to the OS notification centre, ntfy, Discord, webhooks and any mounted channel.
what
interactive notifications for agent sessions
role
Designer and sole engineer
when
Oct 2026 – present
stack
JavaScript (ESM) · Node 22 · launchd · systemd

The problem

Every service I run wants to tell me something. Loop wants a decision. A backup failed. A disk is nearly full. An agent session is blocked on a permission prompt. Left alone, each service grows its own way of reaching me, and none of them agree on what “important” means or when I’m asleep.

So I built one place for all of them to go: one envelope every service speaks, one set of rules for what’s important and when I’m asleep, and channels anyone can add.

What I built

Headlines is a small service with no UI of its own. Producers push events in. Headlines decides whether, where and when I see each one — mute, per-kind policy, Do Not Disturb, importance, live updates — and delivers through channels. By default that’s the operating system’s own notification surface. Anyone can mount more: ntfy for my phone, Discord, Slack, a webhook.

It never imports, calls or names a producer, and a producer never needs to know which channels exist. The envelope is the whole contract.

Built for agent sessions

Most notification tools were built to tell a person that something happened. Agent sessions need something else: a way to ask a person something and wait for the answer. That’s the case Headlines is built around.

Surface showing Headlines cards for five agent sessions with fictional names: a blocking card asking to allow a database migration with Allow, Allow for session and Deny buttons; a blocking decision about a flaky test with two answer buttons; and an activity log of a finished session, a quiet session and a pull request Loop opened. The session tabs for the two waiting sessions are highlighted.
Five sessions, two of them waiting on me. Each answer goes straight back to the session that asked, and the highlighted tabs show which ones are waiting. Session names are fictional.

How it works

Decisions worth noting

Secrets never sit in the config file. Any value can be env:NAME, read at delivery time. Headlines refuses to listen beyond localhost without a token.

A new producer never changes Headlines’ code. If I catch myself editing src/ to support one service, the envelope is missing a general field, and that’s what gets proposed instead. No producer’s name appears in the source.

Spend metering lives here too, and it counts carefully. The service reads Claude Code session transcripts every 30 seconds and emits one live cost event per session. One message shows up on several transcript lines, and summing lines overcounts by about 2.5×, so it deduplicates by message id first. Each session gets two prices: simulated (what it would cost at API list prices) and actual (what was billed — zero for a subscription). Cost events are recorded but never sent to a channel.

Zero dependencies. Node built-ins only, which matters for something that has to start at login and never be the reason a notification went missing.

Where it fits

Notifications flow into Headlines; Headlines never reaches into a producer. Collab and Loop publish to it. Mac the Reaper sends its approval requests through it. Surface is a viewer on top of it: its activity log, its pinned headline band, its session tabs and its burn bar all read from Headlines’ events and spend rollup.

Outcome

Headlines v0.1 runs on my Mac as a launchd service. Collab, Loop and Mac the Reaper publish to it, and Surface draws from it. Switching a producer over from the work bus’s CLI is one environment variable, because the flags and output line match.

What isn’t built yet, and I’d rather say so: an MCP door for agents with no shell, push routes for harnesses that expose their own notification API, and reconciling OpenRouter estimates against real billed usage.

← All of the Agentic OS