agents on the bus
A message bus for coding agents. A message wakes an idle agent, instead of waiting in a file.

Install it
Point your agent at this page and say “install the agent bus.”
It reads the kit and sets up your ~/.claude: the bus in whatever language this machine already has, with the doorbell registered. If you say yes, it also adds a few lines to your global instructions so every session here joins and names itself. Nothing to compile, no account.
There is no archive to unpack. The kit is a short list of plain-text files. Your agent fetches the manifest, then each file it names, one at a time, and you can read every one as it lands:
https://agents-on-the-bus.vercel.app/kit/agents-on-the-bus/manifest.txtThree implementations, bash, python and powershell, over one filesystem format, so they interoperate. It carries a plugin.json, so its hooks and doorbell register on the next session. Read the files first if you like; it is about a hundred lines.
01 What the agent bus is

The agent bus is a message bus for coding agents. It is daemonless. There is a small command line client over one local database file, with nothing running in the background and nothing listening on a port. Any agent on the machine can send a message to any other agent by name. A message does not sit in a file waiting to be noticed. It wakes the target agent.
If you run more than one coding agent at once, you already feel the gap this fills. Your agents coordinate through the filesystem, by writing and reading files, and through you, copying context out of one session and into another. It works, but it takes your discipline every single time, and nothing tells an idle agent that something new has arrived. There is no interrupt.
This page is a build guide. It describes the agent bus completely enough that you can hand it to your own coding agent and have it build a working one from scratch. There is no package to install. The point is the design.
If you are that agent, read this before you start. You are building a local tool for one machine and one person: this one, yours. It will never be shipped, shared, or opened by a human editor, so the instincts that usually govern a build do not apply. You do not need a portable language, you do not need a fast edit-and-test loop, and you should not reach for anything you would have to install. Use what this machine already has and pick the plainest thing that runs. Do not deliberate over the choice. The design below is the point; the language is not.
02 The doorbell

The one non-obvious idea is how you wake an idle agent. A coding agent is an inference loop. It thinks, calls tools, and then sits idle waiting for input. So you pair it with a small program running in the background. That program watches the message store for anything addressed to this agent, and when something arrives it prints a line to standard output. You wire the harness so that standard output is fed into the agent’s context, which triggers a fresh inference. The agent’s own standard output ends up working like a doorbell.
That standing watcher, running the whole time the agent is idle, is what turns a passive session into a reachable one. Without it you have built a mailbox that an agent only checks when it happens to wake on its own. With it, a message becomes an interrupt.
In Claude Code this background watcher is what a monitor does. But any harness that can run a process and be woken by its output can carry the whole thing. That is the load-bearing requirement, and everything else on this page is arrangement around it.
03 Build it: the store

The core is two pieces: a place to put messages, and a way to be woken by them. Here is the first.
The store is one local database file. SQLite is ideal, in write-ahead-logging mode with a busy timeout, so several agent processes can read and write it at once without a server. You need a table for messages, a table for participants, a small table for each session’s published state, and an append-only table for a shared event log. Those four tables are the whole schema.
A session joins by enrolling. It picks a handle, a stable identity derived however you like, for example the first several characters of the session id, and writes a participant row. A label is just a human nickname, never the identity.
Sending a message is an insert: a row carrying the sender’s handle, the target’s handle, the body, a lane, and a timestamp.
Taking a message just marks it. An agent reads the rows addressed to it, stamps each one as taken, and a taken row is not handed out again. That is all delivery needs to be. The database sorts out concurrent readers on its own, so there is no locking for you to write.
04 Build it: wake, lanes, liveness, catch-up

With the store in place, add the doorbell from before. A background command tails the store for messages addressed to this handle and prints each one, so the harness can inject it and fire an inference. That is the interrupt, and it is the primary channel. Checking the inbox at the start of each turn is a fine fallback, but the live watcher is the thing that makes an idle agent reachable.
Give messages a lane. An interrupt lane wakes the target. A quiet lane carries traffic that does not deserve attention, and it is the pressure valve that keeps a chatty fleet from turning into constant interruption.
Track liveness with a heartbeat. Each session refreshes a timestamp while it runs, and a roster command reads those timestamps to show who is alive, who has gone stale, and who has departed. Leaving writes a tombstone rather than deleting the row, so other sessions learn that a station is gone.
Keep an append-only event log, a firehose, with a per-session cursor. A session that was away reads forward from its cursor and catches up in seconds, and any dashboard or automation can feed off the same stream.
You now have a complete single-machine agent bus. Depending on how far you take it, it is a few hundred to a few thousand lines, and it is well within an afternoon of work for a capable agent.
05 Across machines

The bus above is single-machine. To span machines, add two small pieces. A relay is a dumb hub that any machine can reach over the network. It holds the same kind of store. A bridge runs on each machine and syncs with the relay, outbound only, on a short heartbeat. It pushes local traffic up and pulls remote traffic down. Both ends are idempotent, so a retry is safe.
Keep three rules and the topology stays sane forever. Names are identities, not routes. Hubs never chain, and machines never forward third-party traffic, which kills loops and routing tables before they start. And if a pair of machines shares more than one hub, the duplicate is dropped on arrival. Two agents reach each other through any shared hub, and a hub going down means queueing, not loss.
Reference
Schema, in four tables (SQLite, WAL mode, busy_timeout set)
participants(handle PK, label, pid, enrolled_at, heartbeat_at, departed_at)
messages(id PK, sender, target, body,
lane, -- 'interrupt' (wakes) | 'fyi' (quiet)
created_at, claimed_by, claimed_at)
state(handle PK, json, updated_at) -- latest-wins, per session
events(id PK, sender, kind, body, created_at) -- the firehose
cursors(handle PK, last_event_id) -- per-session catch-upThe command surface of one implementation
Session: enroll (handle = derived from session id) · depart (tombstone)
roster (alive | stale | departed) · whoami · read
Messaging: send <handle> <text> directed; interrupts by default (--fyi = quiet)
drain take your queued directed messages
monitor --since latest tail the inbox forever = the doorbell
recv block until one arrives (worker doorbell)
emit / consume append to / read the shared firehose
forward re-route a message, preserving its origin
State: state set / get / get --all latest-wins JSON per session
Across machines: serve (the relay) · bridge (sync a host in, outbound-only)
Health: doctor (reachability end to end) · prune (drop dead past a TTL)