| 📦 Turbo Python 6fc62ea k33g 10h ago | 1 | # Agents et ACP |
| 2 | |
| 3 | Turbo Python est un client de l'[Agent Client Protocol](https://agentclientprotocol.com). Il lance chaque agent comme processus fils et échange avec lui des messages JSON-RPC 2.0 sur son entrée et sa sortie standard, à raison d'un message par ligne. |
| 4 | |
| 5 | ## Où se trouve le fichier |
| 6 | |
| 7 | | Chemin | Lu | Rôle | |
| 8 | | --- | --- | --- | |
| 9 | | `~/.config/turbo-python/acp.toml` | en premier | Les agents que vous voulez dans tous les projets | |
| 10 | | `<projet>/.turbo-python/acp.toml` | en second | Les agents propres à ce projet | |
| 11 | |
| 12 | Les deux sont facultatifs. Quand un `name` d'agent apparaît dans les deux, celui du projet remplace celui de l'utilisateur, étant l'énoncé le plus spécifique — la règle que suivent déjà les [snippets](snippets.md). Un fichier absent n'est pas une erreur ; un fichier présent mais illisible en est une, signalée sous **Agent ▸ Agent status** plutôt que de laisser silencieusement le menu vide. |
| 13 | |
| 14 | `TURBO_PYTHON_DIR` remplace le dossier où le fichier utilisateur est cherché. Le fichier du projet est toujours `.turbo-python/acp.toml` sous le dossier depuis lequel l'éditeur a été lancé — il n'y a pas de remontée dans l'arborescence, pour la même raison que les [réglages de projet](project-settings.md) ne remontent pas. |
| 15 | |
| 16 | ## Format du fichier |
| 17 | |
| 18 | Un bloc `[[agent]]` par agent, dans l'ordre souhaité dans le menu. |
| 19 | |
| 20 | ```toml |
| 21 | [[agent]] |
| 22 | name = "Bob (llama.cpp)" |
| 23 | command = "docker" |
| 24 | args = ["agent", "serve", "acp", ".turbo-python/agent.yaml"] |
| 25 | env = { TELEMETRY_ENABLED = "false" } |
| 26 | cwd = "." |
| 27 | ``` |
| 28 | |
| 29 | | Clé | Type | Requise | Signification | |
| 30 | | --- | --- | --- | --- | |
| 31 | | `name` | chaîne | **oui** | Ce qu'affiche le menu Agent et le titre de la fenêtre. Doit être unique dans l'ensemble fusionné. | |
| 32 | | `command` | chaîne | **oui** | L'exécutable à lancer. Cherché dans `PATH` sauf s'il contient un séparateur. | |
| 33 | | `args` | liste de chaînes | non | Ses arguments, passés tels quels — pas de shell, donc ni guillemets, ni jokers, ni `&&`. | |
| 34 | | `env` | table de chaînes | non | Variables d'environnement ajoutées à celles de l'éditeur. Un nom donné ici l'emporte. | |
| 35 | | `cwd` | chaîne | non | Où le processus démarre, et le `cwd` annoncé à l'agent. Relatif à la racine du projet. Par défaut, la racine du projet. | |
| 36 | |
| 37 | `env` peut aussi s'écrire en sous-table, ce qui est la même chose : |
| 38 | |
| 39 | ```toml |
| 40 | [[agent]] |
| 41 | name = "Bob (llama.cpp)" |
| 42 | command = "docker" |
| 43 | args = ["agent", "serve", "acp", ".turbo-python/agent.yaml"] |
| 44 | |
| 45 | [agent.env] |
| 46 | TELEMETRY_ENABLED = "false" |
| 47 | ``` |
| 48 | |
| 49 | ### Ce qui est refusé |
| 50 | |
| 51 | Le fichier est refusé dans son ensemble, plutôt que chargé à moitié, dès que l'un de ces cas se présente. Un menu à moitié chargé proposant trois de vos cinq agents est pire qu'une erreur qui dit pourquoi. |
| 52 | |
| 53 | | Problème | Message | |
| 54 | | --- | --- | |
| 55 | | un agent sans `name` | `reading …/acp.toml: agent 1 has no name` | |
| 56 | | un agent sans `command` | `reading …/acp.toml: agent "Bob" has no command` | |
| 57 | | deux agents portant le même `name` | `reading …/acp.toml: two agents are called "Bob"` | |
| 58 | | une clé que le format ne définit pas | `reading …/acp.toml: agent.comand is not a key this file has` | |
| 59 | |
| 60 | Le dernier cas est voulu : une clé mal orthographiée silencieusement ignorée ressemblerait exactement à une clé sans effet. |
| 61 | |
| 62 | ## Le menu Agent |
| 63 | |
| 64 | `Alt-A` l'ouvre. Il est sur la barre qu'un agent soit configuré ou non, parce que c'est de là que **Create agents file** doit être atteignable. |
| 65 | |
| 66 | | Entrée | Active quand | Effet | |
| 67 | | --- | --- | --- | |
| 68 | | *une entrée par agent, par son nom* | toujours | Démarrer cet agent et ouvrir une fenêtre dessus | |
| 69 | | **Create agents file** | pas d'`acp.toml` dans le projet | Écrire le fichier de départ et l'ouvrir | |
| 70 | | **Cancel turn** | un tour est en cours dans la fenêtre de devant | `session/cancel` | |
| 71 | | **Agent status** | toujours | Ce qui a été chargé, la ligne de commande de chacun, et ce qui a échoué | |
| 72 | |
| 73 | ## Touches dans une fenêtre agent |
| 74 | |
| 75 | Une fenêtre agent est une fenêtre ordinaire : `F6`, `Alt-1`…`Alt-9`, Tile, Maximise, `[x]` et `[■]` y fonctionnent tous. À l'intérieur : |
| 76 | |
| 77 | | Touche | Effet | |
| 78 | | --- | --- | |
| 79 | | `Entrée` | Envoyer la zone de saisie comme invite | |
| 80 | | `Alt-Entrée` | Insérer un saut de ligne dans la zone de saisie | |
| 81 | | `Tab` | Déplacer le focus entre la conversation et la zone de saisie | |
| 82 | | `Ctrl-C`, `Ctrl-Ins` | Copier la sélection, ou le bloc sur lequel est le curseur | |
| 83 | | `Échap` | Abandonner la sélection ; s'il n'y en a pas, annuler le tour en cours | |
| 84 | | `Ctrl-W` | Fermer la fenêtre et arrêter l'agent | |
| 85 | |
| 86 | Avec la **zone de saisie** au premier plan : |
| 87 | |
| 88 | | Touche | Effet | |
| 89 | | --- | --- | |
| 90 | | `↑` `↓` `←` `→` `Début` `Fin` | Déplacer le curseur dans ce que vous tapez | |
| 91 | | `Retour arrière` `Suppr` | L'éditer ; le retour arrière en début de ligne la joint à celle du dessus | |
| 92 | | `/` en premier caractère | Ouvrir la liste des commandes de l'agent — voir [Commandes et mentions](#commandes-et-mentions) | |
| 93 | | `@` | Ouvrir la liste des fichiers du projet, réduite par ce que vous tapez ensuite | |
| 94 | | `↑` `↓` `PgUp` `PgDn`, liste ouverte | Se déplacer dans la liste | |
| 95 | | `Tab`, liste ouverte | Prendre l'entrée en surbrillance | |
| 96 | | `Entrée`, liste ouverte | Prendre l'entrée en surbrillance ; sur un mot déjà complet, envoyer | |
| 97 | | `Échap`, liste ouverte | Fermer la liste jusqu'à ce que le texte change | |
| 98 | |
| 99 | Avec la **conversation** au premier plan : |
| 100 | |
| 101 | | Touche | Effet | |
| 102 | | --- | --- | |
| 103 | | `↑` `↓` | Déplacer le curseur d'une ligne | |
| 104 | | `PgUp` `PgDn` | Le déplacer d'un écran | |
| 105 | | `Début` `Fin` | Le début de la conversation, et la fin | |
| 106 | | `Shift-` l'une d'elles | Étendre la sélection à la place | |
| 107 | | Glisser avec le bouton 1 | Sélectionner à la main | |
| 108 | | Molette | Défiler de trois lignes sans bouger le curseur | |
| 109 | |
| 110 | Contrairement à une fenêtre terminal, une fenêtre agent ne **prend pas** les raccourcis de l'éditeur : il n'y a pas de shell qui ait besoin de `Ctrl-F`, donc cette touche garde son sens habituel. `Ctrl-C` fait exception, et seulement parce que rien d'autre n'en veut dans une fenêtre agent. |
| 111 | |
| 112 | ## Commandes et mentions |
| 113 | |
| 114 | Deux caractères ouvrent une liste par-dessus le bas de la conversation pendant que vous tapez. Ce sont les deux mêmes que Zed, si bien que la documentation d'un agent — « tapez `/web` pour chercher » — reste vraie ici. |
| 115 | |
| 116 | ### `/` — les commandes de l'agent |
| 117 | |
| 118 | Un agent peut annoncer des commandes par `available_commands_update`, au début de la session ou à tout moment pendant celle-ci. Taper `/` comme **premier caractère** de la zone de saisie les liste : le nom, la description donnée par l'agent et, entre chevrons, ce qu'il attend après le nom quand il attend quelque chose. Continuez à taper pour réduire la liste ; la correspondance porte sur le début du nom et ignore la casse. |
| 119 | |
| 120 | `Tab` complète la commande en surbrillance. Une commande qui prend une entrée est complétée avec une espace à la fin, pour que la suite de votre frappe soit son argument ; une qui n'en prend pas est complétée au nom seul. `Entrée` complète aussi, sauf sur un mot qui se lit déjà exactement comme une commande, où elle envoie. |
| 121 | |
| 122 | Sur le fil, une commande est du **texte** : `/web agent client protocol` part comme un seul bloc texte, et l'agent la reconnaît à son premier mot. C'est tout le protocole des commandes, et c'est pourquoi un `/` ailleurs qu'au début de la zone n'est qu'un caractère. |
| 123 | |
| 124 | Sans commande annoncée, `/` est un caractère et `Tab` garde son sens habituel. **Agent ▸ Agent status** liste les commandes avec leur description. |
| 125 | |
| 126 | ### `@` — un fichier du projet |
| 127 | |
| 128 | Taper `@` n'importe où dans la zone liste les fichiers du projet, relatifs à sa racine, avec des barres obliques. Ce que vous tapez après le `@` réduit la liste : les fichiers dont le nom propre commence par cela viennent d'abord, puis ceux dont le chemin le contient seulement. `Tab` ou `Entrée` complète celui en surbrillance et ajoute une espace. |
| 129 | |
| 130 | À l'envoi de l'invite, chaque `@nom` qui désigne un fichier connu de la liste devient un bloc de contenu **à la place du nom** : |
| 131 | |
| 132 | | L'agent a déclaré | Le bloc envoyé | |
| 133 | | --- | --- | |
| 134 | | `promptCapabilities.embeddedContext: true` | `resource` — l'`uri` du fichier, son `mimeType` et son `text` entier, lu comme `fs/read_text_file` le lit : depuis le tampon ouvert quand le fichier est ouvert et modifié | |
| 135 | | autre chose, ou le fichier n'a pas pu être lu | `resource_link` — l'`uri`, le `name` et le `mimeType`, pour que l'agent aille le chercher lui-même | |
| 136 | |
| 137 | Les mots de part et d'autre partent en blocs texte, si bien que `explique @docs/README.md s'il te plaît` fait trois blocs : `explique `, le fichier, ` s'il te plaît`. La conversation garde la ligne telle que vous l'avez tapée. |
| 138 | |
| 139 | Un mot qui commence par `@` et ne désigne aucun fichier reste du texte — une adresse électronique dans une invite n'est pas un fichier — et `@main.go` ne désigne pas `main.gopher` : le nom doit terminer le mot. |
| 140 | |
| 141 | La liste est le projet parcouru depuis sa racine, `.git` exclu, au plus 5 000 fichiers, et au plus 200 d'entre eux affichés à la fois. Au-delà de l'une ou l'autre limite, tapez une lettre de plus. Le parcours est refait à chaque ouverture de la liste par `@`, si bien qu'un fichier que l'agent vient de créer y figure. |
| 142 | |
| 143 | ## La copie |
| 144 | |
| 145 | La sélection porte sur des **lignes entières**. Rien ne s'édite dans une conversation, donc une demi-ligne n'est jamais ce qu'on veut dire, et des lignes entières préservent l'indentation d'un bloc de code copié. |
| 146 | |
| 147 | Sans rien de sélectionné, la copie prend la **région sur laquelle est le curseur** : un bloc de code délimité, un passage de prose, la sortie d'un appel d'outil. Le libellé d'un interlocuteur et l'en-tête d'un appel d'outil sont du mobilier et forment des régions à part : ni l'un ni l'autre n'est jamais copié avec ce qu'il surmonte. |
| 148 | |
| 149 | L'indentation d'affichage de la conversation est retirée, donc le code collé arrive collé à la marge. |
| 150 | |
| 151 | Le texte part à deux endroits à la fois : |
| 152 | |
| 153 | | Presse-papiers | Comment | Collé avec | |
| 154 | | --- | --- | --- | |
| 155 | | Celui de l'éditeur | directement | `Shift-Ins`, dans un fichier ouvert ici | |
| 156 | | Celui du système | OSC 52, à travers le terminal | `Ctrl-V`, n'importe où ailleurs | |
| 157 | |
| 158 | Rien ne vérifie que le terminal a accepté le second : il n'y a pas de réponse à vérifier, et un terminal peut refuser OSC 52 par sécurité ou demander qu'on l'active. Le presse-papiers de l'éditeur contient le texte dans tous les cas, et la barre d'état dit combien de lignes ont été copiées. |
| 159 | |
| 160 | ## Quelle part du protocole est implémentée |
| 161 | |
| 162 | Version de protocole **1**. Turbo Python annonce sa version dans `initialize` et accepte la version que l'agent répond, pourvu qu'il la connaisse. |
| 163 | |
| 164 | ### Ce que l'éditeur appelle sur l'agent |
| 165 | |
| 166 | | Méthode | Implémentée | Remarques | |
| 167 | | --- | --- | --- | |
| 168 | | `initialize` | oui | Annonce la capacité `fs` ci-dessous ; `terminal` n'est pas annoncée | |
| 169 | | `session/new` | oui | `cwd` vient de la clé `cwd` de l'agent ; `mcpServers` est toujours vide — les serveurs MCP sont l'affaire de l'agent | |
| 170 | | `session/prompt` | oui | Des blocs texte, et un bloc `resource` ou `resource_link` par fichier désigné par `@` — voir [Commandes et mentions](#commandes-et-mentions) | |
| 171 | | `session/cancel` | oui | `Échap`, et **Agent ▸ Cancel turn** | |
| 172 | | `session/load` | **non** | Les conversations ne survivent pas à la fermeture de la fenêtre | |
| 173 | | `authenticate` | **non** | Un agent qui liste des `authMethods` est signalé comme exigeant une connexion que l'éditeur ne sait pas faire | |
| 174 | |
| 175 | ### Ce que l'agent peut appeler sur l'éditeur |
| 176 | |
| 177 | | Méthode | Implémentée | Remarques | |
| 178 | | --- | --- | --- | |
| 179 | | `session/update` | oui | Voir la table ci-dessous | |
| 180 | | `session/request_permission` | oui | Une boîte modale portant les options de l'agent lui-même | |
| 181 | | `fs/read_text_file` | oui | Depuis le tampon quand le fichier est ouvert et modifié, sinon depuis le disque | |
| 182 | | `fs/write_text_file` | oui | Dans le tampon quand le fichier est ouvert, sinon sur le disque | |
| 183 | | `terminal/*` | **non** | Non annoncée, donc un agent conforme ne la demandera pas | |
| 184 | |
| 185 | ### Mises à jour de session |
| 186 | |
| 187 | | `sessionUpdate` | Affiché comme | |
| 188 | | --- | --- | |
| 189 | | `agent_message_chunk` | La réponse de l'agent, ajoutée au fil de son arrivée | |
| 190 | | `agent_thought_chunk` | La même chose, dans la couleur des commentaires, sous une étiquette *réflexion* | |
| 191 | | `user_message_chunk` | Votre propre message, tel que l'agent le renvoie | |
| 192 | | `tool_call` | Une ligne nommant l'outil et son titre, avec son état | |
| 193 | | `tool_call_update` | Repliée sur la ligne dont le `toolCallId` correspond, avec sa sortie | |
| 194 | | `plan` | Les entrées en liste, chacune avec son état | |
| 195 | | `available_commands_update` | La liste que `/` ouvre dans la zone de saisie ; aussi listée, avec les descriptions, par **Agent ▸ Agent status** | |
| 196 | | `usage_update` | Le compte de jetons dans la barre d'état quand la fenêtre est devant | |
| 197 | | tout le reste | Ignoré, et compté ; le compte figure dans **Agent status** | |
| 198 | |
| 199 | Pendant qu'un tour est en cours, la règle entre les deux zones fait tourner un indicateur. Il est dessiné à partir de l'horloge et non d'un compteur, donc deux fenêtres qui réfléchissent en même temps tournent en phase et rien n'a besoin d'être remis à zéro au début d'un tour. Le *titre* de la fenêtre, lui, n'est délibérément pas animé : c'est aussi ce qu'affichent la liste des fenêtres et le menu `Alt`-chiffre, et un nom qui change huit fois par seconde les fait scintiller tous les deux. |
| 200 | |
| 201 | Une mise à jour inconnue est ignorée plutôt que refusée : le protocole grandit, et un éditeur qui cesserait de parler à un agent parce que celui-ci a appris un nouveau type de message aurait tort plus souvent que raison. |
| 202 | |
| 203 | ## Coloration |
| 204 | |
| 205 | La conversation est dessinée avec des clés que tous les thèmes définissent déjà, donc aucun n'a eu besoin d'être touché : |
| 206 | |
| 207 | | Élément | Classe | |
| 208 | | --- | --- | |
| 209 | | Le nom d'un interlocuteur | `syntax.keyword` | |
| 210 | | Une réflexion | `syntax.comment` | |
| 211 | | Un appel d'outil et son état | `syntax.type` | |
| 212 | | Un appel d'outil en échec, et les avis de l'éditeur | `diagnostic.error` | |
| 213 | | Une ligne sélectionnée, et la barre du curseur | `editor.selection` | |
| 214 | | Le code dans un bloc délimité | l'analyseur du langage annoncé | |
| 215 | | Tout le reste | le texte ordinaire de la fenêtre | |
| 216 | |
| 217 | Un bloc délimité annonçant un langage que l'éditeur colore — `python`, `toml`, `yaml`, `markdown`, `javascript`, `html`, `xml`, `dockerfile`, `bash` — est coloré par cet analyseur. Un bloc annonçant autre chose, ou rien, est laissé brut. |
| 218 | |
| 219 | ## Tracer la conversation avec un agent |
| 220 | |
| 221 | | Variable | Effet | |
| 222 | | --- | --- | |
| 223 | | `TURBO_ACP_TRACE=<fichier>` | Ajouter à ce fichier chaque message vers et depuis chaque agent, un par ligne, horodaté et marqué `->` (envoyé) ou `<-` (reçu) | |
| 224 | |
| 225 | C'est pour la seule question à laquelle l'écran ne peut pas répondre — *qu'a réellement envoyé l'agent ?* Une mise à jour que cet éditeur ne sait pas décoder est comptée dans **Agent ▸ Agent status**, qui nomme aussi la dernière et son erreur ; la trace montre le message lui-même. Un fichier qui ne peut pas être ouvert veut dire pas de trace, et rien d'autre : la trace n'a jamais le droit de casser l'éditeur. |
| 226 | |
| 227 | ## Limites |
| 228 | |
| 229 | - **Une session par fenêtre.** Fermer la fenêtre termine la session ; il n'y a pas de reprise. |
| 230 | - **Texte et fichiers seulement.** L'éditeur envoie du texte, et les fichiers que vous désignez par `@` ; ni images ni audio, quoi que disent les `promptCapabilities` de l'agent. |
| 231 | - **Pas d'authentification.** Un agent exigeant une connexion doit être connecté par sa propre CLI avant que l'éditeur ne le lance. |
| 232 | - **`args` n'est pas une commande shell.** `command = "sh"`, `args = ["-c", "…"]` est la façon délibérée d'en obtenir une. |
| 233 | - **Une entrée est plafonnée** à un mégaoctet de texte. Un agent qui déverse tout un journal de compilation ne peut pas rendre la fenêtre inutilisable ; ce qui a été perdu est signalé dans l'entrée elle-même. |
| 234 | |
| 235 | ## Voir aussi |
| 236 | |
| 237 | - La tâche : [Dialoguer avec un agent de code depuis l'éditeur](../how-to/talk-to-an-agent.md) |
| 238 | - Le raisonnement : [Fenêtres agent](../explanation/agent-windows.md) |
| 239 | - Le protocole : [agentclientprotocol.com](https://agentclientprotocol.com) |