|
1 | 1 | # Module agents-features-acp |
| 2 | + |
| 3 | +A module provides integration with [Agent Client Protocol (ACP)](https://agentclientprotocol.com/). |
| 4 | +The main components of the ACP integration in Koog are: |
| 5 | + |
| 6 | +- [**AcpAgent**](src/jvmMain/kotlin/ai/koog/agents/features/acp/AcpAgent.kt): A feature that enables communication |
| 7 | + between Koog agents and ACP-compliant client applications |
| 8 | +- [**MessageConverters**](src/jvmMain/kotlin/ai/koog/agents/features/acp/MessageConverters.kt): Utilities for converting |
| 9 | + messages between Koog and ACP formats |
| 10 | + |
| 11 | +## Overview |
| 12 | + |
| 13 | +### What is ACP? |
| 14 | + |
| 15 | +The Agent Client Protocol (ACP) is a standardized protocol that enables AI agents to communicate with client |
| 16 | +applications through a consistent interface. |
| 17 | +ACP provides a bidirectional communication channel where agents can: |
| 18 | + |
| 19 | +- Receive prompts from clients |
| 20 | +- Send events and updates back to clients in real-time |
| 21 | +- Report tool call status and progress |
| 22 | +- Share reasoning and thoughts with clients |
| 23 | +- Manage session lifecycle and state |
| 24 | + |
| 25 | +To read more about ACP visit [https://agentclientprotocol.com](https://agentclientprotocol.com) |
| 26 | + |
| 27 | +### How is ACP integrated with Koog? |
| 28 | + |
| 29 | +The Koog framework integrates with ACP using the [ACP Kotlin SDK](https://github.com/agentclientprotocol/kotlin-sdk) |
| 30 | +with the additional API extensions presented in the `agents-features-acp` module. |
| 31 | +This integration allows Koog agents to: |
| 32 | + |
| 33 | +1. Communicate with ACP-compliant client applications |
| 34 | +2. Convert between Koog message formats and ACP content blocks |
| 35 | +3. Send real-time updates about agent execution (tool calls, thoughts, completions) |
| 36 | +4. Handle standard ACP events and notifications |
| 37 | + |
| 38 | +### How to Use ACP with Koog? |
| 39 | + |
| 40 | +#### Setting Up an ACP-Enabled Agent |
| 41 | + |
| 42 | +To use ACP with Koog, you need to: |
| 43 | + |
| 44 | +1. Implement the `AgentSupport` and `AgentSession` interface from the ACP SDK |
| 45 | +2. In `AgentSession.prompt` method initialize Koog agent with `AcpAgent` feature installed |
| 46 | +3. Configure the feature with session ID, protocol instance, and events producer |
| 47 | +4. Handle incoming prompts and convert them to Koog messages |
| 48 | + |
| 49 | +Here's a basic example of setting up an ACP-enabled agent: |
| 50 | + |
| 51 | +```kotlin |
| 52 | +class KoogAgentSession( |
| 53 | + override val sessionId: SessionId, |
| 54 | + private val promptExecutor: PromptExecutor, |
| 55 | + private val protocol: Protocol, |
| 56 | + private val clock: Clock, |
| 57 | +) : AgentSession { |
| 58 | + |
| 59 | + private var agentJob: Deferred<Unit>? = null |
| 60 | + private val agentMutex = Mutex() |
| 61 | + |
| 62 | + override suspend fun prompt( |
| 63 | + content: List<ContentBlock>, |
| 64 | + _meta: JsonElement? |
| 65 | + ): Flow<Event> = channelFlow { |
| 66 | + val agentConfig = AIAgentConfig( |
| 67 | + prompt = prompt("acp") { |
| 68 | + system("You are a helpful assistant.") |
| 69 | + }.appendPrompt(content), |
| 70 | + model = OpenAIModels.Chat.GPT4o, |
| 71 | + maxAgentIterations = 1000 |
| 72 | + ) |
| 73 | + |
| 74 | + agentMutex.withLock { |
| 75 | + val agent = AIAgent( |
| 76 | + promptExecutor = promptExecutor, |
| 77 | + agentConfig = agentConfig, |
| 78 | + strategy = yourStrategy(), |
| 79 | + toolRegistry = toolRegistry, |
| 80 | + ) { |
| 81 | + install(AcpAgent) { |
| 82 | + this.sessionId = this@KoogAgentSession.sessionId.value |
| 83 | + this.protocol = this@KoogAgentSession.protocol |
| 84 | + this.eventsProducer = this@channelFlow |
| 85 | + this.setDefaultNotifications = true |
| 86 | + } |
| 87 | + } |
| 88 | + |
| 89 | + agentJob = async { agent.run(Unit) } |
| 90 | + agentJob?.await() |
| 91 | + } |
| 92 | + } |
| 93 | + |
| 94 | + private fun Prompt.appendPrompt(content: List<ContentBlock>): Prompt { |
| 95 | + return withMessages { messages -> |
| 96 | + messages + listOf(content.toKoogMessage(clock)) |
| 97 | + } |
| 98 | + } |
| 99 | +} |
| 100 | +``` |
| 101 | + |
| 102 | +Important notes: |
| 103 | + |
| 104 | +* Use `channelFlow` to allow sending events from different corutines |
| 105 | +* Set `this.setDefaultNotifications = true` to automatically handle standard ACP notifications using agent pipeline |
| 106 | + interseption. In case manual notification handling, please set `this.setDefaultNotifications = false` and process all |
| 107 | + the agents events accoring to the spesificaion using vai protocal `AcpAgent` feature. |
| 108 | +* To convert ACP content blocks to Koog messages use `toKoogMessage` extension function and append recieved user message |
| 109 | + to the prompt. |
| 110 | +* Run the agent in a separate coroutine to allow canceling in `AgentSession.cancel` method |
| 111 | +* Use mutex` to synchronize access to the agent instance, as by current protocol prompt should not trigger new execution |
| 112 | + until previous is finished |
| 113 | + |
| 114 | +#### Configuration Options |
| 115 | + |
| 116 | +The `AcpAgent` feature can be configured through `AcpConfig`: |
| 117 | + |
| 118 | +- `sessionId`: The unique session identifier for the ACP connection |
| 119 | +- `protocol`: The protocol instance used for sending requests and notifications to ACP clients |
| 120 | +- `eventsProducer`: A coroutine-based producer scope for sending events |
| 121 | +- `setDefaultNotifications`: Whether to register default notification handlers (default: `true`) |
| 122 | + |
| 123 | +#### Default Notification Handlers |
| 124 | + |
| 125 | +When `setDefaultNotifications` is enabled, the AcpAgent feature automatically handles: |
| 126 | + |
| 127 | +1. **Agent Completion**: Sends `PromptResponseEvent` with `StopReason.END_TURN` when the agent completes successfully |
| 128 | +2. **Agent Execution Failures**: Sends `PromptResponseEvent` with appropriate stop reasons: |
| 129 | + - `StopReason.MAX_TURN_REQUESTS` for max iterations exceeded |
| 130 | + - `StopReason.REFUSAL` for other execution failures |
| 131 | +3. **LLM Responses**: Converts and sends LLM responses as ACP events (text, tool calls, reasoning) |
| 132 | +4. **Tool Call Lifecycle**: Reports tool call status changes: |
| 133 | + - `ToolCallStatus.IN_PROGRESS` when a tool call starts |
| 134 | + - `ToolCallStatus.COMPLETED` when a tool call succeeds |
| 135 | + - `ToolCallStatus.FAILED` when a tool call fails |
| 136 | + |
| 137 | +#### Sending Custom Events |
| 138 | + |
| 139 | +You can send custom events to the ACP client using the `sendEvent` method: |
| 140 | + |
| 141 | +```kotlin |
| 142 | +val agent = AIAgent(...) { |
| 143 | + install(AcpAgent) { ... } |
| 144 | +} |
| 145 | + |
| 146 | +// Later in your code, access the ACP feature |
| 147 | +withAcpAgent { |
| 148 | + sendEvent( |
| 149 | + Event.SessionUpdateEvent( |
| 150 | + SessionUpdate.PlanUpdate(planEntries) |
| 151 | + ) |
| 152 | + ) |
| 153 | +} |
| 154 | +``` |
| 155 | + |
| 156 | +#### Message Conversion |
| 157 | + |
| 158 | +The module provides utilities for converting between Koog and ACP message formats: |
| 159 | + |
| 160 | +**ACP to Koog:** |
| 161 | + |
| 162 | +```kotlin |
| 163 | +// Convert ACP content blocks to Koog message |
| 164 | +val koogMessage = acpContentBlocks.toKoogMessage(clock) |
| 165 | + |
| 166 | +// Convert single ACP content block to Koog content part |
| 167 | +val contentPart = acpContentBlock.toKoogContentPart() |
| 168 | +``` |
| 169 | + |
| 170 | +**Koog to ACP:** |
| 171 | + |
| 172 | +```kotlin |
| 173 | +// Convert Koog response message to ACP events |
| 174 | +val acpEvents = koogResponseMessage.toAcpEvents() |
| 175 | + |
| 176 | +// Convert Koog content part to ACP content block |
| 177 | +val acpContentBlock = koogContentPart.toAcpContentBlock() |
| 178 | +``` |
| 179 | + |
| 180 | +### Supported Content Types |
| 181 | + |
| 182 | +The ACP integration supports the following content types: |
| 183 | + |
| 184 | +- **Text**: Plain text content |
| 185 | +- **Image**: Image data with MIME type |
| 186 | +- **Audio**: Audio data with MIME type |
| 187 | +- **File**: File attachments (embedded or linked) |
| 188 | + - Base64-encoded binary data |
| 189 | + - Plain text data |
| 190 | + - URL references |
| 191 | +- **Resource**: Embedded resources with URI and content |
| 192 | +- **Resource Link**: Links to external resources |
| 193 | + |
| 194 | +### Platform Support |
| 195 | + |
| 196 | +The ACP feature is currently available only on the JVM platform, as it depends on the ACP Kotlin SDK which is |
| 197 | +JVM-specific. |
| 198 | + |
| 199 | +### Examples |
| 200 | + |
| 201 | +Complete working examples can be found in `examples/simple-examples/src/main/kotlin/ai/koog/agents/example/acp/`. |
| 202 | + |
| 203 | +How to run the example: |
| 204 | +1. Run the AcpApp.kt file |
| 205 | +```shell |
| 206 | +./gradlew :examples:simple-examples:run |
| 207 | +``` |
| 208 | +2. Enter the request for ACP Agent |
| 209 | +```shell |
| 210 | +Move file `my-file.md` to folder `my-folder` and appent title `## My File` to the file content |
| 211 | +``` |
| 212 | +3. Check the events traces in the console |
0 commit comments