Skip to content

Commit f9d4b7a

Browse files
authored
Merge branch 'main' into copilot/fix-8248facb-d165-4c68-a412-2aeaef8641c2
2 parents 8328938 + 935d706 commit f9d4b7a

File tree

180 files changed

+9515
-2506
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

180 files changed

+9515
-2506
lines changed

.codecompanion/acp.md

Lines changed: 346 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,346 @@
1+
# ACP Integration in CodeCompanion.nvim: Comprehensive Overview
2+
3+
This document explains how CodeCompanion.nvim integrates the **Agent Communication Protocol (ACP)** to enable advanced, session-based interactions with Large Language Models (LLMs) and agents. It covers the architecture, workflow, message schema, and how ACP fits into the chat buffer experience.
4+
5+
---
6+
7+
## 1. **What is ACP?**
8+
9+
ACP (Agent Communication Protocol) is a JSON-RPC-based protocol designed for robust, multi-turn, session-oriented communication between clients (like CodeCompanion.nvim) and AI agents. It supports authentication, session management, streaming responses, tool calls, and permission handling.
10+
11+
ACP is schema-driven, with a [JSON schema](llm_notes/acp_json_schema.json) that defines all request, response, and notification types exchanged between client and agent.
12+
13+
---
14+
15+
## 2. **ACP in CodeCompanion.nvim: Architectural Integration**
16+
17+
### **Adapter Pattern**
18+
19+
- **ACPAdapter**: CodeCompanion uses an adapter abstraction (`ACPadapter`) to encapsulate ACP-specific logic. This allows the chat buffer to interact with ACP agents in a unified way, regardless of the underlying agent implementation.
20+
21+
### **Connection Management**
22+
23+
- **ACPConnection**: Handles process spawning, session lifecycle, authentication, and streaming communication with the agent.
24+
- **PromptBuilder**: Fluent API for constructing and sending prompts, handling streamed responses, tool calls, and errors.
25+
26+
### **Chat Buffer Workflow**
27+
28+
1. **User Types Message**: In a Markdown-formatted Neovim buffer, under an H2 header.
29+
2. **Tree-sitter Parsing**: The buffer is parsed to extract the user's message.
30+
3. **ACP Adapter Selection**: The chat buffer uses the ACP adapter if configured.
31+
4. **Session Management**: ACPConnection initializes, authenticates, and creates a session with the agent.
32+
5. **Prompt Submission**: The user's message is sent as a `session/prompt` ACP request.
33+
6. **Streaming Response**: The agent streams back responses, thoughts, and tool calls, which are rendered in the buffer.
34+
7. **Tool Execution & Permissions**: If the agent requests tool execution or permissions, CodeCompanion handles these via ACP notifications and requests.
35+
36+
---
37+
38+
## 3. **ACP Message Flow: Key Steps**
39+
40+
### **Initialization**
41+
42+
- **Client → Agent**: `initialize` request (see `InitializeRequest` in schema)
43+
- Includes client capabilities (e.g., file system access), protocol version.
44+
- **Agent → Client**: `InitializeResponse`
45+
- Returns supported agent capabilities, authentication methods, protocol version.
46+
47+
### **Authentication**
48+
49+
- **Client → Agent**: `authenticate` request (`AuthenticateRequest`)
50+
- Specifies authentication method (e.g., API key).
51+
- **Agent → Client**: `AuthenticateResponse`
52+
- Indicates success/failure.
53+
54+
### **Session Creation**
55+
56+
- **Client → Agent**: `session/new` request (`NewSessionRequest`)
57+
- Includes working directory, MCP servers.
58+
- **Agent → Client**: `NewSessionResponse`
59+
- Returns session ID.
60+
61+
### **Prompting**
62+
63+
- **Client → Agent**: `session/prompt` request (`PromptRequest`)
64+
- Contains session ID and an array of `ContentBlock` objects (parsed from buffer).
65+
- **Agent → Client**: Streaming `SessionNotification`
66+
- Types:
67+
- `agent_message_chunk`: LLM's streamed response.
68+
- `agent_thought_chunk`: LLM's reasoning or plan.
69+
- `tool_call` / `tool_call_update`: Tool execution requests and updates.
70+
- `plan`: High-level execution plan.
71+
- Each notification includes session ID and update payload.
72+
73+
### **Tool Calls & Permissions**
74+
75+
- **Agent → Client**: `session/request_permission` notification
76+
- Requests permission for tool execution, with options (`PermissionOption`).
77+
- **Client → Agent**: `RequestPermissionResponse`
78+
- User selects allow/reject; response sent back to agent.
79+
80+
### **Session Lifecycle**
81+
82+
- **Session Load/Save**: ACP supports loading and saving sessions (`session/load`), enabling persistent conversations.
83+
- **Cancel**: Client can send `CancelNotification` to abort a turn.
84+
85+
---
86+
87+
## 4. **ACP Schema: Key Types and Their Use**
88+
89+
Referencing [`acp_json_schema.json`](llm_notes/acp_json_schema.json):
90+
91+
- **ContentBlock**: Core unit for messages, supporting text, images, audio, resource links, and embedded resources.
92+
- **SessionNotification**: Used for streaming agent responses, thoughts, tool calls, and plans.
93+
- **ToolCall**: Structure for tool execution requests, including kind, status, locations, and content.
94+
- **PermissionOption/RequestPermissionRequest**: Used for interactive permission handling when agent wants to execute tools.
95+
- **StopReason**: Indicates why a turn ended (e.g., success, max tokens, refusal, cancelled).
96+
97+
---
98+
99+
## 5. **CodeCompanion ACP Implementation Details**
100+
101+
### **ACPConnection (lua/codecompanion/acp.lua)**
102+
103+
- **Process Management**: Spawns agent process, manages stdin/stdout, buffers output for JSON-RPC message boundaries.
104+
- **Session State**: Tracks initialization, authentication, session ID, pending responses.
105+
- **Request/Response Handling**: Synchronous requests (initialize, authenticate, session/new), streaming notifications.
106+
- **Permission Handling**: Presents permission dialogs to user, sends outcome to agent.
107+
108+
### **PromptBuilder**
109+
110+
- **Fluent API**: Allows chaining handlers for message chunks, thought chunks, tool calls, completion, and errors.
111+
- **Streaming**: Handles streamed agent responses, updating the chat buffer in real time.
112+
113+
### **Chat Buffer (lua/codecompanion/strategies/chat/init.lua)**
114+
115+
- **Message Parsing**: Uses Tree-sitter to extract user messages and context.
116+
- **ACP Submission**: If ACP adapter is selected, uses ACPConnection to submit prompt and handle streaming responses.
117+
- **Buffer Updates**: Streams agent responses, thoughts, and tool outputs into the buffer under appropriate headers.
118+
119+
---
120+
121+
## 6. **Example ACP Message Exchange**
122+
123+
**User prompt:**
124+
```json
125+
{
126+
"jsonrpc": "2.0",
127+
"id": 42,
128+
"method": "session/prompt",
129+
"params": {
130+
"sessionId": "abc123",
131+
"prompt": [
132+
{ "type": "text", "text": "How do I use the grep_search tool?" }
133+
]
134+
}
135+
}
136+
```
137+
138+
**Agent thinking response:**
139+
```json
140+
{
141+
"jsonrpc": "2.0",
142+
"method": "session/update",
143+
"params": {
144+
"sessionId": "abc123",
145+
"update": {
146+
"sessionUpdate": "agent_thought_chunk",
147+
"content": { "type": "text", "text": "Thinking about how to search for code..." }
148+
}
149+
}
150+
}
151+
```
152+
153+
**Agent response:**
154+
```json
155+
{
156+
"jsonrpc": "2.0",
157+
"method": "session/update",
158+
"params": {
159+
"sessionId": "abc123",
160+
"update": {
161+
"sessionUpdate": "agent_message_chunk",
162+
"content": { "type": "text", "text": "Here are the results of your search..." }
163+
}
164+
}
165+
}
166+
```
167+
168+
**Agent requesting permission with diff:**
169+
```json
170+
{
171+
"jsonrpc": "2.0",
172+
"id": 0,
173+
"method": "session/request_permission",
174+
"params": {
175+
"sessionId": "370030f3-a287-4054-b2a7-010b4bb084e8",
176+
"options": [
177+
{
178+
"optionId": "proceed_always",
179+
"name": "Allow All Edits",
180+
"kind": "allow_always"
181+
},
182+
{
183+
"optionId": "proceed_once",
184+
"name": "Allow",
185+
"kind": "allow_once"
186+
},
187+
{
188+
"optionId": "cancel",
189+
"name": "Reject",
190+
"kind": "reject_once"
191+
}
192+
],
193+
"toolCall": {
194+
"toolCallId": "write_file-1754861800410",
195+
"status": "pending",
196+
"title": "Writing to test.txt",
197+
"content": [
198+
{
199+
"type": "diff",
200+
"path": "test.txt",
201+
"oldText": "This is some old text",
202+
"newText": "This is some new text"
203+
}
204+
],
205+
"locations": [],
206+
"kind": "edit"
207+
}
208+
}
209+
}
210+
```
211+
212+
**Agent requesting permission with no diff**:
213+
214+
```json
215+
{
216+
"jsonrpc": "2.0",
217+
"id": 0,
218+
"method": "session/request_permission",
219+
"params": {
220+
"sessionId": "06d32abb-9ecc-46f3-af80-c5aeb4aafc0c",
221+
"options": [
222+
{
223+
"optionId": "proceed_always",
224+
"name": "Always Allow ls",
225+
"kind": "allow_always"
226+
},
227+
{
228+
"optionId": "proceed_once",
229+
"name": "Allow",
230+
"kind": "allow_once"
231+
},
232+
{
233+
"optionId": "cancel",
234+
"name": "Reject",
235+
"kind": "reject_once"
236+
}
237+
],
238+
"toolCall": {
239+
"toolCallId": "run_shell_command-1755793277864",
240+
"status": "pending",
241+
"title": "ls -F (Lists the files and directories in the current working directory, adding symbols to indicate their type (e.g., a '/' for directories).)",
242+
"content": [],
243+
"locations": [],
244+
"kind": "execute"
245+
}
246+
}
247+
}
248+
```
249+
250+
**Tool call in progress:**
251+
```json
252+
{
253+
"jsonrpc": "2.0",
254+
"method": "session/update",
255+
"params": {
256+
"sessionId": "711e49ae-79d1-4d6e-8481-8844a71f997a",
257+
"update": {
258+
"sessionUpdate": "tool_call",
259+
"toolCallId": "google_web_search-1755377262369",
260+
"status": "in_progress",
261+
"title": "Searching the web for: \"neovim nightly vim.pack.add\"",
262+
"content": [],
263+
"locations": [],
264+
"kind": "search"
265+
}
266+
}
267+
}
268+
```
269+
270+
**Completed tool call:**
271+
```json
272+
{
273+
"jsonrpc": "2.0",
274+
"method": "session/update",
275+
"params": {
276+
"sessionId": "711e49ae-79d1-4d6e-8481-8844a71f997a",
277+
"update": {
278+
"sessionUpdate": "tool_call_update",
279+
"toolCallId": "google_web_search-1755377262369",
280+
"status": "completed",
281+
"content": [
282+
{
283+
"type": "content",
284+
"content": {
285+
"type": "text",
286+
"text": "Search results for \"neovim nightly vim.pack.add\" returned."
287+
}
288+
}
289+
]
290+
}
291+
}
292+
}
293+
```
294+
295+
**fs/write_text_file call**
296+
297+
```json
298+
{
299+
"jsonrpc": "2.0",
300+
"id": 1,
301+
"method": "fs/write_text_file",
302+
"params": {
303+
"path": "/Users/Oli/Code/Neovim/codecompanion.nvim/quotes.lua",
304+
"content": "local quotes = {\n [\"Oli Morris\"] = \"CodeCompanion is the best\",\n [\"Edsger W. Dijkstra\"] = \"Simplicity is a prerequisite for reliability.\",\n [\"Brian Kernighan\"] = \"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.\",\n [\"Linus Torvalds\"] = \"Talk is cheap. Show me the code.\",\n [\"Grace Hopper\"] = \"The most damaging phrase in the language is: 'It's always been done that way.'\",\n}\n\nreturn quotes",
305+
"sessionId": "f13b85cb-fc4e-4bec-902f-95be32325f99"
306+
}
307+
}
308+
```
309+
---
310+
311+
## 7. **Summary Table: ACP Workflow in CodeCompanion**
312+
313+
| Step | ACP Message Type | CodeCompanion Component | Buffer Effect |
314+
|---------------------|-------------------------|-----------------------------|------------------------------|
315+
| Initialization | `initialize` | ACPConnection | Setup agent process/session |
316+
| Authentication | `authenticate` | ACPConnection | Auth dialog if needed |
317+
| Session Creation | `session/new` | ACPConnection | Session ID assigned |
318+
| Prompt Submission | `session/prompt` | PromptBuilder/Chat Buffer | User message sent |
319+
| Streaming Response | `session/update` | PromptBuilder/Chat Buffer | LLM/agent responses streamed |
320+
| Tool Calls | `tool_call`/`tool_call_update` | PromptBuilder/Chat Buffer | Tool execution requests |
321+
| Permission Request | `session/request_permission` | ACPConnection/Chat Buffer | User permission dialog |
322+
| Completion | `PromptResponse` | PromptBuilder/Chat Buffer | End of turn, ready for next |
323+
324+
---
325+
326+
## 8. **Extensibility and Schema Awareness**
327+
328+
- **Schema-Driven**: All ACP interactions are validated and mapped according to the schema, ensuring compatibility and extensibility.
329+
- **Event-Driven**: CodeCompanion fires events for key ACP lifecycle moments (request started, streaming, finished, permission requested), allowing plugins and workflows to hook in.
330+
- **Tooling**: ACP enables rich tool integration, with permission gating and streaming updates.
331+
332+
---
333+
334+
## 9. **References**
335+
336+
- [ACP JSON Schema](llm_notes/acp_json_schema.json)
337+
- `lua/codecompanion/acp.lua` (ACPConnection, PromptBuilder)
338+
- `lua/codecompanion/strategies/chat/init.lua` (Chat Buffer logic)
339+
- [Zed ACP Protocol Reference](https://github.com/zed-industries/agent-protocol)
340+
341+
---
342+
343+
## 10. **Conclusion**
344+
345+
ACP integration in CodeCompanion.nvim provides a powerful, extensible, and schema-driven foundation for conversational AI, tool automation, and session management. By leveraging ACP, CodeCompanion can support advanced agent features, robust tool workflows, and interactive permission handling—all seamlessly integrated into the Neovim chat buffer experience.
346+

0 commit comments

Comments
 (0)