Prime Agent Communication Protocol: How Agents Exchange Messages Internally
Prime Agent implements two complementary communication protocols—the internal Daemon Protocol (JSON-RPC over TCP) and the external ACP (Agent Client Protocol, NDJSON over stdio)—to enable seamless message exchange between sub-agents, supervisors, and external clients.
The Prime Agent framework, developed by PrimeIntellect, separates its agent-to-agent communication into distinct layers: a low-level wire format for internal components and a polished interface for human-facing tools. Both protocols share the same session abstraction identified by a UUID, ensuring consistent state across all interaction modes. This architecture allows sub-agents, daemon clients, and terminal UIs to operate on identical runtime state without synchronization overhead.
Daemon Protocol: Internal Agent-to-Agent Communication
The Daemon Protocol powers all internal communication within Prime Agent. It connects sub-agents, supervisor processes, and headless extensions through a versioned, backward-compatible JSON-RPC transport.
Protocol Specification
The core definition resides in packages/coding-agent/src/modes/daemon/daemon-protocol.ts:
export const DAEMON_PROTOCOL_NAME = "prime-agent.daemon";
export const DAEMON_PROTOCOL_VERSION = 7;
export const DAEMON_PROTOCOL_INFO: DaemonProtocolInfo = {
name: DAEMON_PROTOCOL_NAME,
version: DAEMON_PROTOCOL_VERSION,
};
Key characteristics of the Daemon Protocol:
- Transport: JSON-RPC 2.0 over TCP sockets (or any
Readable/Writablestream pair) - Versioning: Strict backward compatibility via
DAEMON_PROTOCOL_VERSION(currently 7) andDAEMON_SCHEMA_REVISION - Schema identification: Each message carries a
schemaIdformatted asprotocol-${version}-schema-${revision}-${digest} - Connection validation: The daemon rejects clients with newer protocol versions while maintaining support for older clients
Message Types and Session Lifecycle
The protocol defines typed methods and notifications:
| Category | Message | Purpose |
|---|---|---|
| Method | session/new |
Creates a session, returns UUID |
| Method | session/prompt |
Executes a turn, returns stopReason |
| Method | session/cancel |
Aborts ongoing turn |
| Method | session/close |
Terminates session |
| Notification | session/update |
Delivers heartbeats, tool progress, autonomous gate status |
Internal Architecture
The Daemon Protocol wiring follows this flow:
- Daemon startup:
DaemonSupervisor(daemon-supervisor.ts) opens a listening socket - Agent connections:
InProcessAgentConnectionorAgentConnectiontypes instantiateJSONRpcConnection - Message routing: The daemon routes JSON-RPC calls to appropriate
AgentSessionRuntimeinstances - Event propagation: Sub-agents and extensions receive
session/updatenotifications for real-time state changes
Daemon Client Implementation Example
import { jsonRpcClient } from "@agentclientprotocol/sdk";
import { DAEMON_PROTOCOL_INFO } from "@prime-agent/coding-agent/src/modes/daemon/daemon-protocol.js";
async function demo() {
// Connect to the daemon socket
const conn = await jsonRpcClient.connect({ socketPath: "/tmp/prime-agent.sock" });
// Negotiate protocol version
const hello = await conn.request("hello", {});
if (hello.protocol.version !== DAEMON_PROTOCOL_INFO.version) {
throw new Error("Incompatible daemon protocol");
}
// Create a new session
const { sessionId } = await conn.request("session/new", { cwd: process.cwd() });
console.log("Session:", sessionId);
// Send a prompt (plain text, no images)
const { stopReason } = await conn.request("session/prompt", {
sessionId,
prompt: [{ type: "text", text: "Explain quantum tunneling in one sentence." }],
});
console.log("Turn finished, reason:", stopReason);
// Close the session
await conn.request("session/close", { sessionId });
}
demo().catch(console.error);
All identifiers are imported directly from the source to guarantee compile-time safety.
ACP (Agent Client Protocol): External Client Communication
The ACP serves as Prime Agent's public-facing protocol. External clients—terminal UIs, IDE extensions, or automation scripts—consume this interface without needing to understand internal daemon mechanics.
ACP Specification
The implementation lives in packages/coding-agent/src/modes/acp/acp-mode.ts:
| Aspect | Implementation |
|---|---|
| Transport | NDJSON over stdio (or in-memory streams in tests) |
| SDK | @agentclientprotocol/sdk |
| Meta envelope | _meta field in acp-meta.ts for Prime Agent-specific data |
| Capabilities | Advertised via initialize: image, embeddedContext, session.close |
The _meta envelope hides daemon-only details from standard ACP clients while preserving critical internal state:
- Autonomous execution status
- Heartbeat timestamps
- Working directory mismatches
ACP Mode Operation
The runAcpMode function orchestrates external client handling:
- Stdout hijacking:
takeOverStdoutredirects all logging tostderr, keeping the NDJSON stream pristine - Connection bridging: Creates
InProcessAgentConnectionattached to the currentAgentSessionRuntime - RPC handler setup:
acp.agent({ name: "prime-agent" })builds the method dispatch table - Request mapping:
initialize→ returns protocol version and capability flagssession/new→ records active session, subscribes to internal eventssession/prompt→ splits ACP blocks into text/images, callsconnection.promptAndWaitsession/cancel→ triggersAbortControllerfor ongoing turnsession/close→ aborts, unsubscribes, disposes connection
ACP Client Implementation Example
import * as acp from "@agentclientprotocol/sdk";
async function acpDemo() {
// NDJSON over stdio (works with `prime-agent.sh --acp`)
const stream = acp.ndJsonStream(process.stdout, process.stdin);
const client = acp.agent({ name: "my-client" }).connect(stream);
// Initialize – learn version & capabilities
const init = await client.request("initialize", {});
console.log("Protocol version:", init.protocolVersion);
console.log("Agent capabilities:", init.agentCapabilities);
// Create a session
const { sessionId } = await client.request("session/new", {});
console.log("Session created:", sessionId);
// Send a prompt (text + optional image)
const prompt = [
{ type: "text", text: "Write a short haiku about sunrise." },
// { type: "image", data: "...base64...", mimeType: "image/png" },
];
const { stopReason } = await client.request("session/prompt", { sessionId, prompt });
console.log("Prompt finished, stopReason:", stopReason);
// Close the session
await client.request("session/close", { sessionId });
}
acpDemo().catch(console.error);
The client automatically receives session/update notifications for tool execution progress and autonomous gate status.
How Daemon Protocol and ACP Interact
Prime Agent's dual-protocol design enables flexible deployment without state duplication.
External Client ACP (NDJSON) Prime Agent
(acp SDK) <------------------> (acp-mode.ts)
^ |
| v
JSON-RPC (Daemon) +---------------------+
(daemon-protocol.ts) | AgentSessionRuntime |
| | (shared state) |
+------------------------------>+---------------------+
Key integration points:
- ACP mode internally uses Daemon Protocol: When running
--acp, Prime Agent creates anInProcessAgentConnectionthat speaks Daemon Protocol to the sameAgentSessionRuntime - Shared session model: Both protocols reference identical session state, tool registries, and autonomous execution status
- Event fan-out: Internal events from
agent.ts(turn_start,tool_execution_start,tool_execution_end,turn_end) propagate throughacp-events.tsto ACP clients assession/updatepayloads
This means a sub-agent spawned via Daemon Protocol and a prompt issued via ACP both operate on the same underlying state machine—no synchronization code required.
Practical Code Patterns
Starting Prime Agent in ACP Mode
# CLI entry point
./prime-agent.sh --acp
# Invokes runAcpMode(runtimeHost) from:
# packages/coding-agent/src/modes/acp/acp-mode.ts
Spawning Sub-Agents Programmatically
import { InProcessAgentConnection } from "./agent-connection/in-process-agent-connection.js";
async function spawnSubagent(runtime: AgentSessionRuntime) {
const conn = new InProcessAgentConnection(runtime);
const { sessionId } = await conn.request("session/new", { cwd: process.cwd() });
await conn.request("session/prompt", {
sessionId,
prompt: [{ type: "text", text: "List files in the current directory." }],
});
}
Monitoring Daemon Events
import { DaemonSupervisor } from "./daemon-supervisor.js";
const supervisor = new DaemonSupervisor({ socketPath: "/tmp/prime-agent.sock" });
supervisor.on("session_event", (event) => {
if (event.type === "tool_execution_end") {
console.log(`Tool ${event.toolName} finished with result:`, event.result);
}
});
await supervisor.start();
Source File Reference
| Component | File Path | Purpose |
|---|---|---|
| Daemon protocol spec | packages/coding-agent/src/modes/daemon/daemon-protocol.ts |
DAEMON_PROTOCOL_INFO, version constants, type definitions |
| Daemon supervisor | packages/coding-agent/src/modes/daemon/daemon-supervisor.ts |
Socket lifecycle, connection management |
| In-process connection | packages/coding-agent/src/modes/agent-connection/in-process-agent-connection.ts |
Runtime-to-protocol bridge without network |
| ACP mode | packages/coding-agent/src/modes/acp/acp-mode.ts |
NDJSON transport, request handlers |
| ACP event mapping | packages/coding-agent/src/modes/acp/acp-events.ts |
AgentEvent → ACP payload conversion |
| ACP meta envelope | packages/coding-agent/src/modes/acp/acp-meta.ts |
Namespaced _meta field definitions |
| Session runtime | packages/coding-agent/src/core/agent-session-runtime.ts |
Shared state machine |
| Agent loop | packages/agent/src/agent.ts |
Core event emission |
Summary
- Daemon Protocol provides versioned JSON-RPC (
prime-agent.daemonv7) for internal agent-to-agent communication, with strict backward compatibility and schema validation - ACP offers NDJSON over stdio for external clients, built on
@agentclientprotocol/sdk, with capability negotiation and a hidden_metaenvelope - Both protocols share
AgentSessionRuntime, ensuring consistent state whether you connect via raw socket or interactive terminal - Session lifecycle (
session/new→session/prompt→session/close) is identical across both interfaces, withsession/updatenotifications keeping clients synchronized
Frequently Asked Questions
What is the current Daemon Protocol version in Prime Agent?
The current version is 7, defined as DAEMON_PROTOCOL_VERSION in daemon-protocol.ts. The daemon validates client versions on connection, rejecting newer clients while maintaining backward compatibility with older ones.
Can I connect to Prime Agent without using the ACP SDK?
Yes. You can implement the Daemon Protocol directly by speaking JSON-RPC 2.0 over a TCP socket or Readable/Writable stream. Import DAEMON_PROTOCOL_INFO from daemon-protocol.ts to ensure version compatibility, then call methods like session/new and session/prompt.
How does Prime Agent handle protocol version mismatches?
The daemon enforces version compatibility through DAEMON_PROTOCOL_VERSION and DAEMON_SCHEMA_REVISION. During the initial hello exchange, the daemon compares the client's declared version against its own. Clients running older protocol versions continue to function; clients with newer versions are rejected with an error.
What information travels in the ACP _meta envelope?
The _meta field, defined in acp-meta.ts, carries Prime Agent-specific data that standard ACP clients can ignore. This includes autonomous execution status, heartbeat timestamps, working directory mismatches, and sub-agent spawn notifications. It allows the public protocol to remain generic while supporting rich internal features.
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 →