How the Hive Coordination Layer Handles Inter-Agent Messaging and Routing in munder-difflin
The Hive coordination layer uses a poll-based file-system router that scans agent outboxes every ~1.5 seconds and atomically moves JSON messages to recipient inboxes using renameSync, guaranteeing reliable inter-agent delivery without native file watchers.
The inter-agent messaging and routing system in munder-difflin is powered by the Hive, an on-disk, multi-agent coordination subsystem managed entirely by the main Electron process. All communication between agents flows through this layer, which persists state under <harnessHome>/hive/ and enforces a single-committer Git model.
What Is the Hive Coordination Layer?
The Hive is the central nervous system for agent-to-agent communication in munder-difflin. Located under <harnessHome>/hive/, it is orchestrated by the HiveManager class in src/main/hive.ts and bootstrapped from src/main/index.ts. Rather than using network sockets or message brokers, the Hive relies on an on-disk directory structure and atomic file operations to route messages between agents.
Agent Workspace Layout
Every agent registered with the Hive receives its own dedicated folder under agents/<id>/. According to the HiveManager definition in src/main/hive.ts (lines 1–10), each workspace contains:
inbox/– Directory for incoming messages.outbox/– Directory for outgoing messages.identity.md– Agent identity document.memory.md– Persistent agent memory.
This layout ensures that agents only need file-system access to participate in the messaging network.
HiveMessage Format
Messages exchanged between agents are plain JSON objects conforming to the HiveMessage interface declared in src/main/hive.ts (lines 48–63). A HiveMessage includes metadata fields such as from, to, act, subject, body, timestamps, and routing controls like hops and requires_reply.
The router treats any file ending in .json inside an outbox/ as a valid message payload.
Poll-Based Router and Routing Mechanism
The core routing logic is implemented as a poll-based router running on a setInterval timer (routerTimer). As implemented in src/main/hive.ts around lines 1311–1350, the router executes the following cycle approximately every 1.5 seconds:
- Enumerates every registered agent.
- Reads all
.jsonfiles inside the agent'soutbox/. - Parses each file into a
HiveMessageand inspects themsg.tofield. - Atomically moves the file to
agents/<recipient>/inbox/usingfs.renameSync. - Archives the original file under the sender's
outbox/.sentusingfs.renameSync.
The design deliberately avoids fs.watch to sidestep macOS quirks and to guarantee atomicity through simple file operations.
Delivery Guarantees and Atomicity
The Hive coordination layer provides three key reliability mechanisms:
- Atomic move –
renameSyncensures a message is never duplicated or left partially written during transit. - Sent archive – The
outbox/.sentdirectory preserves a permanent record of dispatched messages, which the UI can render later. - Cursor tracking – A
cursor.jsonfile tracks inbox consumption, guaranteeing each message is processed exactly once (used by the Stop hook).
These mechanisms allow the system to recover cleanly from crashes without message loss.
Human-in-the-Loop Routing
Messages addressed to human are automatically intercepted by the router and redirected to the special "god" agent. The god agent's UI surfaces a permission prompt, allowing human operators to approve or reject actions inline. This routing decision happens inside the standard router loop without requiring a separate approval queue.
Hook Integration for Claude-Code Agents
Agents that expose a HIVE_SOCK via the sockPath() method receive lifecycle hook payloads such as Stop and PreToolUse. The router does not directly manage these hook calls; instead, it ensures that any message generated by a hook finishes its lifecycle and appears in the recipient's inbox. The hook shim writes JSON payloads to the Unix domain socket, and the Hive layer translates those into standard outbox files for the next router tick.
Step-by-Step Message Flow
A complete round-trip through the Hive inter-agent messaging pipeline looks like this:
- Agent writes a JSON file to its own
outbox/using any filename ending in.json. - Router tick scans the
outbox/of every registered agent. - For each message file:
- The router reads the JSON and validates the
HiveMessagestructure. - It resolves the recipient from the
tofield. - It performs an atomic
renameSyncto move the file intoagents/<recipient>/inbox/. - It archives the original under the sender's
outbox/.sent/.
- The router reads the JSON and validates the
- Inbox consumer—whether a Claude-Code hook, the UI, or a custom skill—reads the file from the recipient's inbox, processes it, and optionally writes a response to its own
outbox/. - The router picks up the response on the next tick, completing the round-trip.
Code Examples
Sending a Message from an Agent
Agents produce messages by writing JSON files directly to their outbox:
import { writeFileSync, join } from 'node:fs';
import { randomBytes } from 'node:crypto';
const msg = {
id: randomBytes(6).toString('hex'),
conversation: 'conv-123',
in_reply_to: null,
from: 'agentA',
to: 'agentB',
act: 'request',
subject: 'Need data',
body: 'Please fetch the latest sales numbers.',
hops: 0,
requires_reply: true,
needs_human: false,
created_at: new Date().toISOString()
};
const outboxDir = join(
process.env.HIVE_ROOT!,
'agents',
'agentA',
'outbox'
);
writeFileSync(
join(outboxDir, `${msg.id}.json`),
JSON.stringify(msg, null, 2)
);
When the next router tick runs, this file is moved to agents/agentB/inbox/ and archived under agents/agentA/outbox/.sent/.
Reading a Received Message
Recipients consume inbox messages by reading the JSON files from their inbox/ directory:
import { readdirSync, readFileSync, join } from 'node:fs';
const inboxDir = join(process.env.HIVE_ROOT!, 'agents', 'agentB', 'inbox');
for (const file of readdirSync(inboxDir)) {
if (!file.endsWith('.json')) continue;
const raw = readFileSync(join(inboxDir, file), 'utf8');
const msg = JSON.parse(raw) as HiveMessage;
console.log('Got message:', msg.subject, msg.body);
}
Hook-Driven Automatic Routing
For Claude-Code agents, the system generates a settings.json that points the hook shim to HIVE_SOCK. The shim writes JSON payloads to this socket, which the Hive layer surfaces as standard outbox files. The poll-based router then picks them up on its next tick without requiring any additional routing code.
Key Files in the Messaging Pipeline
src/main/hive.ts– DefinesHiveMessage,HiveManager, the poll-based router loop, and archive helpers.src/main/index.ts– BootstrapsHiveManager, starts therouterTimer, and integrates the Hive with the Electron main process.src/shared/agentProvider.ts– Determines whether a provider can receive inbox messages (canReceiveInbox) and whether it is hive-aware.src/main/pty.ts– Spawns agent PTYs with environment variables such asHIVE_ROOTandAGENT_DIRthat the router relies on.HIVE.md– Design document covering the on-disk layout, the single-committer Git model, and the rationale for avoiding file watchers.
Summary
- The Hive coordination layer handles inter-agent messaging and routing in munder-difflin through an on-disk, poll-based file-system router.
- All messages are plain JSON conforming to the
HiveMessageinterface, stored in per-agentinbox/andoutbox/directories. - The router in
src/main/hive.tsusesrenameSyncto atomically move messages from sender outboxes to recipient inboxes every ~1.5 seconds. - Delivery guarantees include atomic moves, a
outbox/.sentarchive, andcursor.jsontracking for exactly-once processing. - Human-in-the-loop messages route to the "god" agent automatically, while Claude-Code hooks leverage
HIVE_SOCKfor lifecycle integration.
Frequently Asked Questions
How does the Hive router avoid message loss during crashes?
The router relies on fs.renameSync for both delivery and archiving. Because rename is atomic on POSIX systems, a message either remains in the sender's outbox or arrives completely in the recipient's inbox—there is no intermediate state. After delivery, the original is archived under outbox/.sent, creating a durable record even if the process restarts.
Why does the Hive use polling instead of file watchers?
According to the source comments in src/main/hive.ts, the poll-based router avoids fs.watch to eliminate macOS-specific quirks and to guarantee atomicity. A simple setInterval timer scanning every ~1.5 seconds is deterministic, portable, and avoids race conditions that can occur with native file-system event APIs.
Can agents communicate without writing files directly?
Yes. Agents using Claude-Code can write to the HIVE_SOCK Unix domain socket exposed via sockPath(). The Hive layer translates these socket payloads into outbox files, which the poll-based router then delivers using the standard inbox move. This allows hook-driven agents to participate without managing the file system directly.
What happens when a message is addressed to a human operator?
When msg.to equals human, the router redirects the message to the special "god" agent. The god agent's UI presents a permission prompt inline, enabling human approval without a separate queue or routing bypass. This is handled inside the standard router loop in src/main/hive.ts.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →