turbo-editors/turbo-pythonpublic Fork 0
v1.0.2
Commits
Clone
git clone https://git.rickub.com/turbo-editors/turbo-python.git
git clone ssh://git@rickub.com/turbo-editors/turbo-python.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

acp.md · 239 lines · 14.1 KBmarkdown Blame HistoryRaw
📦 Turbo Python 6fc62ea k33g 12h ago1# Agents and ACP
2
3Turbo Python is a client for the [Agent Client Protocol](https://agentclientprotocol.com). It starts each agent as a child process and exchanges JSON-RPC 2.0 messages with it over stdin and stdout, one message per line.
4
5## Where the file lives
6
7| Path | Read | Purpose |
8| --- | --- | --- |
9| `~/.config/turbo-python/acp.toml` | first | Agents you want in every project |
10| `<project>/.turbo-python/acp.toml` | second | Agents belonging to this project |
11
12Both are optional. Where an agent's `name` appears in both, the project's replaces the user's, being the more specific statement — the same rule [snippets](snippets.md) follow. A missing file is not an error; a file that is present but unreadable is, and is reported under **Agent ▸ Agent status** rather than silently leaving the menu empty.
13
14`TURBO_PYTHON_DIR` overrides the directory the user-level file is looked for in. The project file is always `.turbo-python/acp.toml` under the directory the editor was started in — there is no walk up the tree, for the same reason [project settings](project-settings.md) do not walk up.
15
16## File format
17
18One `[[agent]]` block per agent, in the order you want them in the menu.
19
20```toml
21[[agent]]
22name = "Bob (llama.cpp)"
23command = "docker"
24args = ["agent", "serve", "acp", ".turbo-python/agent.yaml"]
25env = { TELEMETRY_ENABLED = "false" }
26cwd = "."
27```
28
29| Key | Type | Required | Meaning |
30| --- | --- | --- | --- |
31| `name` | string | **yes** | What the Agent menu shows and what the window is titled. Must be unique within the merged set. |
32| `command` | string | **yes** | The executable to run. Looked up on `PATH` unless it contains a separator. |
33| `args` | list of strings | no | Its arguments, passed as given — no shell, so no quoting, globbing or `&&`. |
34| `env` | table of strings | no | Environment variables added to the ones the editor was started with. A name given here wins. |
35| `cwd` | string | no | Where the process starts, and the `cwd` the agent is told about. Relative to the project root. Defaults to the project root. |
36
37`env` may also be written as a sub-table, which is the same thing:
38
39```toml
40[[agent]]
41name = "Bob (llama.cpp)"
42command = "docker"
43args = ["agent", "serve", "acp", ".turbo-python/agent.yaml"]
44
45[agent.env]
46TELEMETRY_ENABLED = "false"
47```
48
49### What is refused
50
51The file is refused as a whole, rather than partly loaded, when any of these hold. A half-loaded menu offering three of your five agents is worse than an error saying why.
52
53| Problem | Message |
54| --- | --- |
55| an agent with no `name` | `reading …/acp.toml: agent 1 has no name` |
56| an agent with no `command` | `reading …/acp.toml: agent "Bob" has no command` |
57| two agents with the same `name` | `reading …/acp.toml: two agents are called "Bob"` |
58| a key the format does not define | `reading …/acp.toml: agent.comand is not a key this file has` |
59
60The last one is deliberate: a misspelt key that was quietly ignored would look exactly like one that had no effect.
61
62## The Agent menu
63
64`Alt-A` opens it. It is on the bar whether or not any agent is configured, because that is where **Create agents file** has to be reachable from.
65
66| Item | Enabled when | Effect |
67| --- | --- | --- |
68| *one item per agent, by name* | always | Start that agent and open a window on it |
69| **Create agents file** | no `acp.toml` in the project | Write the starter file and open it |
70| **Cancel turn** | a turn is running in the front window | `session/cancel` |
71| **Agent status** | always | What was loaded, what each command line is, and what failed |
72
73## Keys inside an agent window
74
75An agent window is an ordinary window: `F6`, `Alt-1``Alt-9`, Tile, Maximise, `[x]` and `[■]` all work on it. Inside it:
76
77| Key | Effect |
78| --- | --- |
79| `Enter` | Send the input box as a prompt |
80| `Alt-Enter` | Insert a newline in the input box |
81| `Tab` | Move focus between the conversation and the input box |
82| `Ctrl-C`, `Ctrl-Ins` | Copy the selection, or the block the cursor is on |
83| `Esc` | Drop the selection; with none, cancel the turn in progress |
84| `Ctrl-W` | Close the window and stop the agent |
85
86With the **input box** focused:
87
88| Key | Effect |
89| --- | --- |
90| `↑` `↓` `←` `→` `Home` `End` | Move the cursor in what you are typing |
91| `Backspace` `Delete` | Edit it; backspace at the start of a line joins it to the one above |
92| `/` as the first character | Open the list of the agent's commands — see [Commands and mentions](#commands-and-mentions) |
93| `@` | Open the list of the project's files, narrowed by what you type after it |
94| `↑` `↓` `PgUp` `PgDn`, list open | Move through the list |
95| `Tab`, list open | Take the highlighted entry |
96| `Enter`, list open | Take the highlighted entry; on a word that is already complete, send |
97| `Esc`, list open | Close the list until the text changes |
98
99With the **conversation** focused:
100
101| Key | Effect |
102| --- | --- |
103| `↑` `↓` | Move the cursor one line |
104| `PgUp` `PgDn` | Move it a screenful |
105| `Home` `End` | The start of the conversation, and the end |
106| `Shift-` any of those | Extend the selection instead |
107| Drag with button 1 | Select by hand |
108| Wheel | Scroll three lines, leaving the cursor where it is |
109
110Unlike a terminal window, an agent window does **not** take the editor's shortcuts: there is no shell to need `Ctrl-F`, so it keeps its usual meaning. `Ctrl-C` is the exception, and only because nothing else in an agent window wants it.
111
112## Commands and mentions
113
114Two characters open a list over the bottom of the conversation while you type. They are the same two Zed uses, so an agent's own documentation — "type `/web` to search" — holds here too.
115
116### `/` — the agent's commands
117
118An agent may announce commands with `available_commands_update`, at the start of the session or at any point during it. Typing `/` as the **first character** of the box lists them: the name, the agent's description, and, in angle brackets, what it expects after the name when it expects something. Keep typing to narrow the list; the match is on the start of the name and ignores case.
119
120`Tab` completes the highlighted command. A command that takes input is completed with a trailing space, so the next thing you type is its argument; one that takes none is completed to the bare name. `Enter` completes too, except on a word that already reads exactly as a command, where it sends.
121
122On the wire a command is **text**: `/web agent client protocol` goes out as one text block, and the agent recognises it by its first word. That is the whole protocol for commands, and it is why a `/` anywhere but the start of the box is just a character.
123
124With no commands announced, `/` is a character and `Tab` keeps its ordinary meaning. **Agent ▸ Agent status** lists the commands with their descriptions.
125
126### `@` — a file from the project
127
128Typing `@` anywhere in the box lists the project's files, relative to the project root with forward slashes. What you type after the `@` narrows the list: files whose own name begins with it come first, then files whose path merely contains it. `Tab` or `Enter` completes the highlighted one and adds a space.
129
130When the prompt is sent, each `@name` that names a file the list knew becomes a content block **in place of the name**:
131
132| The agent declared | The block sent |
133| --- | --- |
134| `promptCapabilities.embeddedContext: true` | `resource` — the file's `uri`, `mimeType` and full `text`, read the way `fs/read_text_file` reads it: from the open buffer when the file is open and modified |
135| anything else, or the file could not be read | `resource_link` — the `uri`, `name` and `mimeType`, for the agent to fetch itself |
136
137The words either side go as text blocks, so `explain @docs/README.md please` is three blocks: `explain `, the file, ` please`. The conversation keeps the line as you typed it.
138
139A word that begins with `@` and names no file stays text — an e-mail address in a prompt is not a file — and `@main.go` does not name `main.gopher`: the name has to end the word.
140
141The list is the project walked from its root, `.git` left out, at most 5 000 files, and at most 200 of them shown at once. Past either limit, type one more letter. It is walked afresh each time `@` opens the list, so a file the agent just created is in it.
142
143## Copying
144
145Selection is by **whole lines**. Nothing in a conversation is edited, so half a line is never what somebody means, and whole lines keep a copied code block's indentation intact.
146
147With nothing selected, copying takes the **region the cursor is on**: one fenced code block, one passage of prose, one tool call's output. A speaker's label and a tool call's heading are furniture and are regions of their own, so neither is ever copied with what it sits above.
148
149The indentation the conversation is drawn with is removed, so pasted code is flush.
150
151The text goes to two places at once:
152
153| Clipboard | How | Pasted with |
154| --- | --- | --- |
155| The editor's | directly | `Shift-Ins`, into a file open here |
156| The system's | OSC 52, through the terminal | `Ctrl-V`, anywhere else |
157
158Nothing checks whether the terminal accepted the second: there is no reply to check, and a terminal may refuse OSC 52 for security or need it turned on. The editor's own clipboard has the text either way, and the status bar says how many lines were copied.
159
160## How much of the protocol is implemented
161
162Protocol version **1**. Turbo Python sends its version in `initialize` and accepts whatever version the agent answers with, provided it is one it knows.
163
164### What the editor calls on the agent
165
166| Method | Implemented | Notes |
167| --- | --- | --- |
168| `initialize` | yes | Advertises the `fs` capability below; `terminal` is not advertised |
169| `session/new` | yes | `cwd` from the agent's `cwd` key; `mcpServers` is always empty — MCP servers are the agent's own business |
170| `session/prompt` | yes | Text blocks, and one `resource` or `resource_link` block per file named with `@` — see [Commands and mentions](#commands-and-mentions) |
171| `session/cancel` | yes | `Esc`, and **Agent ▸ Cancel turn** |
172| `session/load` | **no** | Conversations do not survive closing the window |
173| `authenticate` | **no** | An agent that lists `authMethods` is reported as needing a login the editor cannot perform |
174
175### What the agent may call on the editor
176
177| Method | Implemented | Notes |
178| --- | --- | --- |
179| `session/update` | yes | See the table below |
180| `session/request_permission` | yes | A modal dialog carrying the agent's own options |
181| `fs/read_text_file` | yes | From the open buffer when the file is open and modified, otherwise from disk |
182| `fs/write_text_file` | yes | Into the open buffer when the file is open, otherwise to disk |
183| `terminal/*` | **no** | Not advertised, so a conforming agent will not ask |
184
185### Session updates
186
187| `sessionUpdate` | Shown as |
188| --- | --- |
189| `agent_message_chunk` | The agent's reply, appended as it arrives |
190| `agent_thought_chunk` | The same, in the comment colour, under a *thinking* label |
191| `user_message_chunk` | Your own message, as the agent echoes it back |
192| `tool_call` | A line naming the tool and its title, with its status |
193| `tool_call_update` | Folded onto the line the `toolCallId` matches, carrying its output |
194| `plan` | The entries as a list, each with its status |
195| `available_commands_update` | The list `/` opens in the input box; also listed, with descriptions, by **Agent ▸ Agent status** |
196| `usage_update` | The token count on the status bar while the window is in front |
197| anything else | Ignored, and counted; the count is in **Agent status** |
198
199While a turn is running, the rule between the panes turns a spinner. It is drawn from the clock rather than from a counter, so two windows thinking at once turn in step and nothing has to be reset when a turn begins. The window's *title* deliberately does not animate: it is also what the window list and the `Alt`-digit menu show, and a name changing eight times a second makes both flicker.
200
201An unknown update is ignored rather than refused: the protocol grows, and an editor that stopped talking to an agent because it learnt a new kind of message would be wrong more often than it was right.
202
203## Colouring
204
205The conversation is drawn with keys every theme already sets, so none of them needed touching:
206
207| Part | Class |
208| --- | --- |
209| A speaker's name | `syntax.keyword` |
210| A thought | `syntax.comment` |
211| A tool call and its status | `syntax.type` |
212| A failed tool call, and the editor's own notices | `diagnostic.error` |
213| A selected line, and the cursor's bar | `editor.selection` |
214| Code inside a fence | the scanner for the fence's language |
215| Everything else | the window's plain text |
216
217A fenced block naming a language the editor colours — `python`, `toml`, `yaml`, `markdown`, `javascript`, `html`, `xml`, `dockerfile`, `bash` — is coloured by that scanner. One naming anything else, or nothing at all, is left plain.
218
219## Tracing the conversation with an agent
220
221| Variable | Effect |
222| --- | --- |
223| `TURBO_ACP_TRACE=<file>` | Append every message to and from every agent to that file, one per line, stamped with the time and marked `->` (sent) or `<-` (received) |
224
225It is for the one question the screen cannot answer — *what did the agent actually send?* An update this editor cannot decode is counted under **Agent ▸ Agent status**, which also names the last one and its error; the trace shows the message itself. A file that cannot be opened means no trace and nothing else: the trace is never allowed to break the editor.
226
227## Limits
228
229- **One session per window.** Closing the window ends the session; there is no resume.
230- **Text and files only.** The editor sends text, and the files you name with `@`; not images or audio, whatever the agent's `promptCapabilities` say.
231- **No authentication.** An agent that requires a login must be logged in by its own CLI before the editor starts it.
232- **`args` are not a shell command.** `command = "sh"`, `args = ["-c", "…"]` is how to get one deliberately.
233- **One entry is capped** at a megabyte of text. An agent printing a whole build log cannot make the window unusable; what was dropped is said in the entry itself.
234
235## See also
236
237- The task: [How to talk to a coding agent from the editor](../how-to/talk-to-an-agent.md)
238- The reasoning: [Agent windows](../explanation/agent-windows.md)
239- The protocol: [agentclientprotocol.com](https://agentclientprotocol.com)