Agent Communication Protocol
Relevant source files
The following files were used as context for generating this wiki page:The Agent Communication Protocol is an event-driven system that facilitates communication between the core Agent logic and its various consumers, such as a Terminal User Interface (TUI), a Web UI, or a Slack bot. It is built on a publish-subscribe model where the Agent class emits a series of AgentEvent objects representing its lifecycle and state changes. This decouples the agent's core processing from its presentation, allowing for flexible and diverse front-end implementations.
The protocol's primary function is to provide real-time updates as the agent interacts with an LLM, executes tools, and processes messages. Consumers subscribe to the agent and react to these events to render UI components, log activities, or trigger other actions.
Sources: packages/agent/src/agent.ts:10-14, packages/coding-agent/src/tui/tui-renderer.ts:468-479, packages/mom/src/agent.ts:517-520
Architecture Overview
The communication architecture consists of three main parts: the Agent, the AgentTransport, and the Consumer (subscriber). The Agent manages the state and orchestrates the process, the Transport handles the direct communication with the LLM provider, and the Consumer listens to events to update the user-facing interface.
This diagram illustrates the high-level data flow between the components.
Core Components
| Component | Description | Relevant Files |
|---|---|---|
Agent Class | The central class that manages state (AgentState), handles user prompts, executes tools, and emits events. It provides a subscribe method for consumers. | packages/agent/src/agent.ts, packages/web-ui/src/agent/agent.ts |
AgentTransport | An interface responsible for communicating with the LLM provider. The ProviderTransport is the primary implementation that uses the @mariozechner/pi-ai library. | packages/agent/src/agent.ts:18-20 |
AgentEvent | A set of typed objects representing different stages of the agent's lifecycle, such as starting a prompt, streaming a message, or executing a tool. | packages/coding-agent/src/tui/tui-renderer.ts:503-605 |
| Consumer | Any component that subscribes to the agent's events to perform actions, like rendering a UI. Examples include TuiRenderer and the mom Slack bot's agent runner. | packages/coding-agent/src/tui/tui-renderer.ts, packages/mom/src/agent.ts |
Sources: packages/agent/src/agent.ts, packages/coding-agent/src/tui/tui-renderer.ts, packages/mom/src/agent.ts
Prompt Execution Lifecycle
The core of the communication protocol is the event stream generated during a prompt execution. When a consumer calls agent.prompt(), a sequence of events is emitted, allowing for real-time feedback.
The following sequence diagram shows the typical flow of events during a prompt that involves tool use.
Agent Events
The agent communicates its state through a series of AgentEvent types. Consumers subscribe to these events to update their state accordingly.
| Event Type | Description | Key Properties |
|---|---|---|
agent_start | Fired when agent.prompt() is called and processing begins. | type |
message_start | Fired when a new message (user or assistant) begins. | type, message |
message_update | Fired multiple times as an assistant message streams in. | type, message |
message_end | Fired when a message is fully received. The message is added to the agent's state. | type, message |
tool_execution_start | Fired when the agent begins executing a tool called by the LLM. | type, toolCallId, toolName, args |
tool_execution_end | Fired when a tool execution completes, successfully or with an error. | type, toolCallId, toolName, result, isError |
agent_end | Fired when the entire prompt-response cycle is complete. | type |
bash_end | (RPC Mode Only) Fired when a manually triggered bash command completes. | type, message |
compaction | (RPC Mode Only) Fired when a context compaction operation completes. | type, summary, tokensBefore, tokensAfter |
Sources: packages/coding-agent/src/tui/tui-renderer.ts:503-605, packages/mom/src/agent.ts:518-688, packages/coding-agent/test/rpc.test.ts:251-255
Aborting Communication
The agent's execution can be interrupted by calling the agent.abort() method. This sends a signal to the AbortController, which cancels the underlying transport request to the LLM. The agent then emits a final message_end event with a stopReason of "aborted" and an errorMessage.
Sources: packages/agent/src/agent.ts:119-121, packages/agent/test/e2e.test.ts:149-153
State Management
The Agent class maintains a state object (AgentState) that represents its current configuration and message history. This state is exposed to consumers via the agent.state getter.
AgentState Interface
The AgentState contains all the necessary information about the agent's current context.
| Property | Type | Description |
|---|---|---|
systemPrompt | string | The system prompt sent to the LLM. |
model | Model | The LLM model configuration. |
thinkingLevel | ThinkingLevel | The level of reasoning/thinking to request from the LLM. |
tools | AgentTool[] | The list of tools available to the LLM. |
messages | AppMessage[] | The history of messages in the conversation. |
isStreaming | boolean | Flag indicating if the agent is currently processing a prompt. |
streamMessage | Message | null | The partial assistant message being streamed. |
pendingToolCalls | Set<string> | A set of IDs for tool calls that are currently executing. |
error | string | undefined | An error message if the last operation failed. |
Sources: packages/agent/src/agent.ts:24-36, packages/web-ui/src/agent/agent.ts:25-35
Message Handling
Message Transformation
The agent distinguishes between application-level messages (AppMessage) and LLM-compatible messages (Message). A messageTransformer function is used to convert AppMessage objects, which may contain application-specific data like UI attachments, into a format the LLM can process. The default transformer handles image and document attachments.
// packages/agent/src/agent.ts:10-46
function defaultMessageTransformer(messages: AppMessage[]): Message[] {
return messages
.filter((m) => {
// Only keep standard LLM message roles
return m.role === "user" || m.role === "assistant" || m.role === "toolResult";
})
.map((m) => {
if (m.role === "user") {
const { attachments, ...rest } = m as any;
// ... attachment conversion logic ...
}
// ...
});
}
Sources: packages/agent/src/agent.ts:10-46, packages/web-ui/example/src/main.ts:200-201
Message Queuing
The agent can queue user messages that arrive while it is already processing a prompt. The queueMode setting determines how these queued messages are handled.
one-at-a-time(default): Sends one queued message per turn.all: Sends all queued messages at once in the next turn.
The TuiRenderer uses this feature to allow users to type and submit new prompts while the agent is working.
Sources: packages/agent/src/agent.ts:54-56, packages/agent/src/agent.ts:102-108, packages/coding-agent/src/tui/tui-renderer.ts:449-465
RPC Mode
For headless or programmatic use, the agent can run in RPC (Remote Procedure Call) mode. In this mode, all communication happens over stdin and stdout using JSON-serialized objects.
- Output (
stdout): The agent emits allAgentEventobjects as newline-delimited JSON strings. - Input (
stdin): The agent listens for newline-delimited JSON commands.
This flow diagram shows the RPC communication loop.
The following commands are supported via stdin:
| Command | type | Description |
|---|---|---|
| Prompt | prompt | Submits a new message to the agent. Expects a message property. |
| Abort | abort | Aborts the current agent operation. |
| Compact | compact | Triggers a manual context compaction. |
| Bash | bash | Executes a shell command and adds the result to the message history. Expects a command property. |
Sources: packages/coding-agent/src/main.ts:1040-1123, packages/coding-agent/test/rpc.test.ts:104-105