# How Inter-Agent Communication Enables Direct Messaging Between Running Agents in Prime Agent

> Discover how Prime Agent's inter agent communication uses a central daemon as a message broker to enable direct JSON-RPC messaging between subagents via asynchronous streams.

- Repository: [Prime Intellect/prime-agent](https://github.com/PrimeIntellect-ai/prime-agent)
- Tags: deep-dive
- Published: 2026-08-18

---

**Prime Agent uses a central daemon process as a message broker that routes typed JSON-RPC payloads between registered subagents via asynchronous streams, enabling direct addressing through unique subagent IDs.**

The PrimeIntellect-ai/prime-agent repository implements a sophisticated inter-agent communication layer that allows running agents to exchange messages directly. This system relies on a central daemon to broker connections, manage registration, and route typed messages between subagent processes using asynchronous streams. By leveraging the **Subagent Registry** and **Message Router**, agents can perform direct messaging, broadcasts, and request-response calls through a type-safe `Message` interface.

## The Architecture Behind Inter-Agent Communication

At the heart of Prime Agent’s communication model is a daemon that maintains the state of all active agents. When a subagent launches, it does not communicate peer-to-peer; instead, every message flows through the daemon’s centralized broker.

### The Daemon and Subagent Registry

The **Subagent Registry**, implemented in [`packages/agent/src/subagent_registry.ts`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/packages/agent/src/subagent_registry.ts), maintains the mapping between active agents and their communication endpoints. When an agent starts, it calls `registerSubagent()` to announce itself to the daemon. The registry assigns the agent a unique **subagent ID** and stores a reference to the agent’s inbound message stream. This registration step is mandatory; an agent cannot receive messages until it exists in the registry.

### The Message Router and Asynchronous Streams

The **Message Router** in [`packages/agent/src/message_router.ts`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/packages/agent/src/message_router.ts) handles the actual forwarding of payloads. It accepts JSON-RPC-style requests of type `sendMessage`, which contain two critical fields:

- `targetId` – the subagent ID of the destination agent.
- `payload` – a typed `Message` object (e.g., text, tool call, or system event).

The router looks up the target’s inbound queue in the registry and pushes the payload onto that queue. Because the system uses asynchronous streams, the router provides back-pressure handling and guarantees ordered delivery of messages.

## How Direct Messaging Works

Direct messaging between running agents is a two-step process that combines registration with targeted routing.

1. **Registration** – An agent calls `registerSubagent()` (implemented in `SubagentRegistry`) through the `DaemonClient` and receives its unique `agentId`.

2. **Message Transmission** – The sending agent invokes `daemonClient.sendMessage(senderId, targetId, message)`. The daemon validates the IDs against the registry, routes the message to the target’s queue, and the recipient receives it via its `on('message', …)` listener.

## Communication Patterns: Broadcast and Request-Response

While direct messaging addresses a single target, the same infrastructure supports broader interaction patterns:

- **Broadcast** – The `sendMessageAll` method routes a payload to every registered subagent simultaneously.
- **Request-Response** – Agents can use `await daemonClient.call(targetId, request)` to send a message and await a reply, enabling synchronous-style coordination between asynchronous processes.

All messages conform to the `Message` interface defined in [`packages/agent/src/message.ts`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/packages/agent/src/message.ts), ensuring type-safe payloads across different agent implementations.

## Code Implementation Examples

### Setting Up Agents and Sending Messages

To initiate communication, create a `DaemonClient` connection and register two agents. The client connects to the UNIX socket the daemon listens on, then assigns unique IDs to each subagent.

```ts
// In a script that controls the Prime Agent runtime
import { DaemonClient } from 'packages/agent/src/daemon_client';
import { Message } from 'packages/agent/src/message';

// Create a daemon client (connects to the UNIX socket the daemon listens on)
const daemon = new DaemonClient();

// Launch two sub-agents (the daemon returns a unique ID for each)
const aliceId = await daemon.registerSubagent({ name: 'alice' });
const bobId   = await daemon.registerSubagent({ name: 'bob' });

// Set up a listener on Bob to receive messages
daemon.onMessage(bobId, (msg: Message) => {
  console.log('Bob got:', msg);
});

// Alice sends a direct text message to Bob
const textMsg: Message = { type: 'text', content: 'Hello, Bob!' };
await daemon.sendMessage(aliceId, bobId, textMsg);

```

### Handling Incoming Messages in Sub-Agents

Sub-agents extend the base `Agent` class from [`packages/agent/src/agent.ts`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/packages/agent/src/agent.ts), which provides convenient methods for sending and receiving. Incoming messages trigger the `message` event, allowing the agent to react and respond.

```ts
import { Agent } from 'packages/agent/src/agent';

class EchoAgent extends Agent {
  constructor(id: string) {
    super(id);
    this.on('message', this.handleMessage);
  }

  private async handleMessage(msg: Message) {
    if (msg.type === 'text') {
      // Echo the text back to the sender
      await this.sendMessage(msg.from, {
        type: 'text',
        content: `Echo: ${msg.content}`,
      });
    }
  }
}

```

### Key Source Files

| File | Role |
|------|------|
| [`packages/agent/src/daemon_client.ts`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/packages/agent/src/daemon_client.ts) | Connects agents to the daemon, exposes `registerSubagent`, `sendMessage`, and listener registration. |
| [`packages/agent/src/subagent_registry.ts`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/packages/agent/src/subagent_registry.ts) | Holds the mapping of subagent IDs to inbound queues; responsible for registration and lookup. |
| [`packages/agent/src/message_router.ts`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/packages/agent/src/message_router.ts) | Core routing logic that forwards messages between registered subagents. |
| [`packages/agent/src/message.ts`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/packages/agent/src/message.ts) | Type definitions for all supported message shapes (`text`, `tool_call`, `event`, etc.). |
| [`packages/agent/src/agent.ts`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/packages/agent/src/agent.ts) | Base class that sub-agents extend; provides convenient `sendMessage` and `on('message')` helpers. |

## Summary

- Prime Agent relies on a **central daemon** that acts as a message broker for all active subagents.
- Agents must **register** via `registerSubagent()` in [`packages/agent/src/subagent_registry.ts`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/packages/agent/src/subagent_registry.ts) to receive a unique ID and message queue.
- The **Message Router** in [`packages/agent/src/message_router.ts`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/packages/agent/src/message_router.ts) routes payloads by looking up the target’s inbound stream and pushing messages with ordered delivery guarantees.
- The system supports **direct messaging**, **broadcasting** via `sendMessageAll`, and **request-response** patterns via `call()`.
- All communication uses the **type-safe `Message` interface** to ensure consistent payload structures across the agent ecosystem.

## Frequently Asked Questions

### How does Prime Agent ensure ordered message delivery?

The Message Router in [`packages/agent/src/message_router.ts`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/packages/agent/src/message_router.ts) guarantees ordered delivery by pushing payloads onto the target’s inbound queue using asynchronous streams. The router also implements back-pressure handling to prevent overwhelming slow consumers, ensuring that messages arrive in the sequence they were sent.

### What message types can agents exchange?

According to the `Message` interface defined in [`packages/agent/src/message.ts`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/packages/agent/src/message.ts), agents can exchange structured payloads including `text` messages, `tool_call` requests, system `event` notifications, and other typed objects. This interface ensures that both the sender and receiver agree on the payload structure.

### Can an agent send a message to multiple recipients at once?

Yes. Beyond direct one-to-one messaging, agents can use the `sendMessageAll` method available on the `DaemonClient`. This broadcasts the payload to all currently registered subagents by iterating through the Subagent Registry and enqueuing the message in every active inbound stream.

### How does a subagent receive its unique ID?

When a subagent process starts, it instantiates a `DaemonClient` and calls `registerSubagent()`. This method communicates with the daemon’s `SubagentRegistry` (located in [`packages/agent/src/subagent_registry.ts`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/packages/agent/src/subagent_registry.ts)) to assign a unique subagent ID, which the daemon returns to the caller for use in subsequent send operations.