How AionUi Implements Multi-Agent Coordination via the ACP Protocol
AionUi achieves seamless multi-agent coordination by treating every AI back-end as a plug-in that speaks the Agent Communication Protocol (ACP), using a unified adapter layer to normalize streaming messages, tool calls, and status updates from Gemini CLI, Claude Code, Codex, and other compatible agents.
The AionUi repository provides a unified chat interface that coordinates multiple AI coding assistants through a standardized communication layer. By implementing multi-agent coordination via the ACP protocol, the application abstracts vendor-specific implementations into a single message format, allowing users to switch between Gemini CLI, Claude Code, Codex, and future ACP-compatible tools without modifying the core UI logic.
The ACP Protocol Architecture
Standardizing AI Back-Ends Behind ACP
Instead of maintaining separate integration logic for each AI vendor, AionUi requires every external agent to implement the Agent Communication Protocol (ACP). This JSON-based messaging standard defines a consistent interface for streaming text chunks, tool invocations, and session updates. By enforcing this protocol, AionUi treats diverse back-ends as interchangeable plug-ins, eliminating the need for vendor-specific rendering code in the UI layer.
Core Coordination Components
Three primary classes manage the ACP integration and enable multi-agent coordination:
- AcpAdapter: Located in
src/agent/acp/AcpAdapter.ts, this class normalizes all ACP events into the internalTMessageformat used by the UI. It handles message chunking, tool-call tracking, and plan rendering. - AcpConnection: Found in
src/agent/acp/AcpConnection.ts, this class manages the lifecycle of external agent processes. It constructsnpxcommands to spawn ACP bridges and parses JSON message streams from stdin/stdout. - AcpDetector: Defined in
src/agent/acp/AcpDetector.ts, this utility inspects backend capabilities to verify ACP support before attempting instantiation, preventing runtime errors with incompatible agents.
Implementing Multi-Agent Coordination in AionUi
Agent Selection and Mode Constants
The UI exposes available agents through a centralized constants file. The src/renderer/constants/agentModes.ts file defines the selectable modes including Gemini, Claude, Codex, and other supported back-ends【/cache/repos/github.com/iOfficeAI/AionUi/main/src/renderer/constants/agentModes.ts#L26-L33】.
When a user initiates a chat, the application generates a unique conversation ID and passes it to the agent's ACP adapter. The AcpAdapter constructor stores this ID and uses it to tag every UI message, ensuring proper conversation threading【/cache/repos/github.com/iOfficeAI/AionUi/main/src/agent/acp/AcpAdapter.ts#L15-L22】.
The AcpAdapter Translation Layer
The AcpAdapter class serves as the central translation engine for multi-agent coordination. Its convertSessionUpdate method processes incoming ACP events and dispatches them to specialized handlers based on the update type:
- agent_message_chunk: Converts streaming text fragments into incremental UI updates【/cache/repos/github.com/iOfficeAI/AionUi/main/src/agent/acp/AcpAdapter.ts#L52-L60】
- agent_thought_chunk: Handles tip messages and reasoning streams separately from main content【/cache/repos/github.com/iOfficeAI/AionUi/main/src/agent/acp/AcpAdapter.ts#L62-L70】
- tool_call / tool_call_update: Creates unified tool invocation messages that preserve the same
msg_idacross updates, enabling the UI to merge streaming results into a single component【/cache/repos/github.com/iOfficeAI/AionUi/main/src/agent/acp/AcpAdapter.ts#L74-L92】 - plan: Renders structured plan components for agent orchestration workflows【/cache/repos/github.com/iOfficeAI/AionUi/main/src/agent/acp/AcpAdapter.ts#L94-L102】
Process Spawning with AcpConnection
The AcpConnection class manages the external process lifecycle required for multi-agent coordination. It constructs platform-specific npx commands to launch ACP bridge packages for each supported agent. For example, it executes npx @anthropic/claude-acp for Claude Code or npx @google/gemini-acp for Gemini CLI【/cache/repos/github.com/iOfficeAI/AionUi/main/src/agent/acp/AcpConnection.ts#L230-L265】.
The class handles stdin/stdout piping and JSON stream parsing, converting raw process output into structured ACP updates that feed into the AcpAdapter. Before spawning, AcpDetector validates that the selected backend supports ACP by inspecting its capabilities【/cache/repos/github.com/iOfficeAI/AionUi/main/src/agent/acp/AcpDetector.ts#L40-L58】.
Tool-Call Lifecycle Management
To maintain UI consistency during multi-agent coordination, AcpAdapter implements sophisticated tool-call tracking. The resetMessageTracking method generates fresh UUIDs for new AI responses, ensuring that streaming chunks accumulate under a single message identifier【/cache/repos/github.com/iOfficeAI/AionUi/main/src/agent/acp/AcpAdapter.ts#L29-L31】【/cache/repos/github.com/iOfficeAI/AionUi/main/src/agent/acp/AcpAdapter.ts#L68-L71】.
When a tool is invoked, createOrUpdateAcpToolCall stores the call in an activeToolCalls map using the toolCallId as the key. It returns a message with msg_id set to this ID, ensuring that subsequent updates target the same UI element【/cache/repos/github.com/iOfficeAI/AionUi/main/src/agent/acp/AcpAdapter.ts#L72-L83】. The updateAcpToolCall method merges status and content updates, cleaning up completed calls after 60 seconds to prevent memory leaks【/cache/repos/github.com/iOfficeAI/AionUi/main/src/agent/acp/AcpAdapter.ts#L84-L99】.
Multi-Agent Coordination Code Examples
Creating an ACP-Enabled Agent (Gemini CLI Example)
The Gemini integration demonstrates how specific agents wrap the generic ACP infrastructure:
// src/agent/gemini/index.ts (conceptual structure)
import { AcpAdapter } from '@/agent/acp/AcpAdapter';
import { AcpConnection } from '@/agent/acp/AcpConnection';
export class GeminiAgent {
private adapter: AcpAdapter;
private connection: AcpConnection;
constructor(conversationId: string) {
// 'gemini-cli' tells AcpConnection which npm bridge to run
this.adapter = new AcpAdapter(conversationId, 'gemini-cli');
this.connection = new AcpConnection(this.adapter);
}
async sendPrompt(prompt: string) {
// Forward the prompt to the external Gemini process
await this.connection.send({ type: 'prompt', content: prompt });
}
// The connection streams ACP updates; the adapter turns them into UI messages
onMessage(callback: (msgs: TMessage[]) => void) {
this.connection.onSessionUpdate((update) => {
const uiMsgs = this.adapter.convertSessionUpdate(update);
callback(uiMsgs);
});
}
}
Key implementation detail: The constructor invokes new AcpAdapter(conversationId, 'gemini-cli'), establishing the backend identifier that AcpConnection uses to construct the correct npx command.
Handling Tool-Call Merging
The following excerpt from AcpAdapter demonstrates how tool calls maintain identity across updates:
// src/agent/acp/AcpAdapter.ts (excerpt)
private createOrUpdateAcpToolCall(update: ToolCallUpdate): IMessageAcpToolCall {
const toolCallId = update.update.toolCallId;
const baseMessage = {
id: uuid(),
msg_id: toolCallId, // Critical: same ID for merging
conversation_id: this.conversationId,
createdAt: Date.now(),
position: 'left' as const,
};
const acpToolCallMessage: IMessageAcpToolCall = {
...baseMessage,
type: 'acp_tool_call',
content: update,
};
this.activeToolCalls.set(toolCallId, acpToolCallMessage);
return acpToolCallMessage;
}
This pattern ensures that when the ACP bridge streams tool_call_update events, the UI updates the existing message bubble rather than creating duplicates【/cache/repos/github.com/iOfficeAI/AionUi/main/src/agent/acp/AcpAdapter.ts#L72-L83】.
Runtime Agent Switching
The UI layer can instantiate different agents without protocol-specific logic:
// Conceptual UI switching logic
function switchAgent(mode: AgentMode, conversationId: string) {
switch (mode) {
case 'gemini':
return new GeminiAgent(conversationId);
case 'claude':
return new AcpAgent(conversationId, 'claude');
case 'codex':
return new AcpAgent(conversationId, 'codex');
default:
throw new Error(`Unsupported agent mode: ${mode}`);
}
}
The AcpAgent class (located in src/agent/acp/index.ts) serves as the generic wrapper for any ACP-compliant backend, constructing the adapter with new AcpAdapter(this.id, this.extra.backend)【/cache/repos/github.com/iOfficeAI/AionUi/main/src/agent/acp/index.ts#L100-L108】.
Key Source Files for Multi-Agent Coordination
| File | Purpose |
|---|---|
src/agent/acp/AcpAdapter.ts |
Core translator that converts ACP events to UI messages; handles tool-call merging and streaming state. |
src/agent/acp/AcpConnection.ts |
Manages external process lifecycle, constructs npx commands, and parses JSON streams from agent bridges. |
src/agent/acp/AcpDetector.ts |
Validates ACP support before spawning agents to prevent runtime errors with incompatible tools. |
src/agent/acp/index.ts |
Generic AcpAgent wrapper used by Claude Code, Codex, and other ACP-compliant back-ends. |
src/agent/gemini/index.ts |
Gemini-specific wrapper that instantiates AcpAdapter with the 'gemini-cli' backend identifier. |
src/agent/codex/index.ts |
Codex-specific wrapper utilizing the same AcpAdapter infrastructure. |
src/renderer/constants/agentModes.ts |
Defines the UI-selectable agent modes (Gemini, Claude, Codex, etc.). |
src/process/services/mcpServices/McpService.ts |
Central service that registers all agents (including ACP agents) and routes messages. |
Summary
- AionUi achieves multi-agent coordination by treating every AI back-end as an ACP-compliant plug-in, eliminating the need for vendor-specific UI code.
- The AcpAdapter class in
src/agent/acp/AcpAdapter.tsnormalizes all ACP events (message chunks, tool calls, plans) into a unifiedTMessageformat. - AcpConnection handles process lifecycle management, spawning external agents via
npxcommands and parsing JSON streams over stdin/stdout. - Tool-call integrity is maintained through persistent
msg_idtracking, ensuring that streaming updates merge into single UI components rather than fragmenting the chat history. - The architecture supports runtime agent switching through a mode selector defined in
src/renderer/constants/agentModes.ts, allowing users to move between Gemini CLI, Claude Code, Codex, and future ACP-compatible tools without restarting the application.
Frequently Asked Questions
What is the ACP protocol in AionUi?
The Agent Communication Protocol (ACP) is a JSON-based messaging standard that AionUi uses to communicate with external AI agents. It defines a consistent interface for streaming text chunks, tool invocations, and status updates, allowing the application to treat diverse back-ends like Gemini CLI, Claude Code, and Codex as interchangeable plug-ins.
How does AionUi handle different AI agents without code duplication?
AionUi eliminates duplication through the AcpAdapter pattern. Each agent (Gemini, Claude, Codex) instantiates the same AcpAdapter class with a unique backend identifier (e.g., 'gemini-cli', 'claude'). The adapter translates all ACP-specific events into the internal TMessage format, meaning the UI layer only needs to understand one message structure regardless of which AI is running.
What happens when an AI tool calls a function during a conversation?
When an ACP-compatible agent invokes a tool, the bridge emits a tool_call update. The AcpAdapter creates a message with a msg_id matching the toolCallId, stores it in an activeToolCalls map, and renders it in the UI. Subsequent tool_call_update events target the same msg_id, allowing the UI to stream progress, output, or errors into the existing message bubble rather than creating duplicates. Completed calls are cleaned up after 60 seconds.
Can AionUi support new AI agents without modifying the core codebase?
Yes. Because AionUi uses the ACP protocol as an abstraction layer, adding support for a new AI agent only requires that the agent expose an ACP-compliant bridge (typically an npm package). The existing AcpConnection class can spawn the new agent via npx, and the AcpAdapter will automatically translate its messages. No changes are needed in the UI rendering layer or message handling logic, provided the new agent adheres to the ACP message schema.
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 →