# How AionUi Implements Multi-Agent Coordination via the ACP Protocol

> Discover how AionUi enables multi-agent coordination with Gemini CLI Claude Code and Codex using the ACP protocol. Explore a unified adapter layer for seamless AI tool integration via iOfficeAI AionUi.

- Repository: [OfficeAI/AionUi](https://github.com/iofficeai/aionui)
- Tags: deep-dive
- Published: 2026-02-19

---

**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`](https://github.com/iOfficeAI/AionUi/blob/main/src/agent/acp/AcpAdapter.ts), this class normalizes all ACP events into the internal `TMessage` format used by the UI. It handles message chunking, tool-call tracking, and plan rendering.
- **AcpConnection**: Found in [`src/agent/acp/AcpConnection.ts`](https://github.com/iOfficeAI/AionUi/blob/main/src/agent/acp/AcpConnection.ts), this class manages the lifecycle of external agent processes. It constructs `npx` commands to spawn ACP bridges and parses JSON message streams from stdin/stdout.
- **AcpDetector**: Defined in [`src/agent/acp/AcpDetector.ts`](https://github.com/iOfficeAI/AionUi/blob/main/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`](https://github.com/iOfficeAI/AionUi/blob/main/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_id` across 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:

```typescript
// 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:

```typescript
// 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:

```typescript
// 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`](https://github.com/iOfficeAI/AionUi/blob/main/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`](https://github.com/iOfficeAI/AionUi/blob/main/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`](https://github.com/iOfficeAI/AionUi/blob/main/src/agent/acp/AcpConnection.ts) | Manages external process lifecycle, constructs `npx` commands, and parses JSON streams from agent bridges. |
| [`src/agent/acp/AcpDetector.ts`](https://github.com/iOfficeAI/AionUi/blob/main/src/agent/acp/AcpDetector.ts) | Validates ACP support before spawning agents to prevent runtime errors with incompatible tools. |
| [`src/agent/acp/index.ts`](https://github.com/iOfficeAI/AionUi/blob/main/src/agent/acp/index.ts) | Generic `AcpAgent` wrapper used by Claude Code, Codex, and other ACP-compliant back-ends. |
| [`src/agent/gemini/index.ts`](https://github.com/iOfficeAI/AionUi/blob/main/src/agent/gemini/index.ts) | Gemini-specific wrapper that instantiates `AcpAdapter` with the `'gemini-cli'` backend identifier. |
| [`src/agent/codex/index.ts`](https://github.com/iOfficeAI/AionUi/blob/main/src/agent/codex/index.ts) | Codex-specific wrapper utilizing the same `AcpAdapter` infrastructure. |
| [`src/renderer/constants/agentModes.ts`](https://github.com/iOfficeAI/AionUi/blob/main/src/renderer/constants/agentModes.ts) | Defines the UI-selectable agent modes (Gemini, Claude, Codex, etc.). |
| [`src/process/services/mcpServices/McpService.ts`](https://github.com/iOfficeAI/AionUi/blob/main/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.ts`](https://github.com/iOfficeAI/AionUi/blob/main/src/agent/acp/AcpAdapter.ts) normalizes all ACP events (message chunks, tool calls, plans) into a unified `TMessage` format.
- **AcpConnection** handles process lifecycle management, spawning external agents via `npx` commands and parsing JSON streams over stdin/stdout.
- Tool-call integrity is maintained through persistent `msg_id` tracking, 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`](https://github.com/iOfficeAI/AionUi/blob/main/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.