# Agents et ACP Turbo Go 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. ## Où se trouve le fichier | Chemin | Lu | Rôle | | --- | --- | --- | | `~/.config/turbo-go/acp.toml` | en premier | Les agents que vous voulez dans tous les projets | | `/.turbo-go/acp.toml` | en second | Les agents propres à ce projet | 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. `TURBO_GO_DIR` remplace le dossier où le fichier utilisateur est cherché. Le fichier du projet est toujours `.turbo-go/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. ## Format du fichier Un bloc `[[agent]]` par agent, dans l'ordre souhaité dans le menu. ```toml [[agent]] name = "Bob (llama.cpp)" command = "docker" args = ["agent", "serve", "acp", ".turbo-go/agent.yaml"] env = { TELEMETRY_ENABLED = "false" } cwd = "." ``` | Clé | Type | Requise | Signification | | --- | --- | --- | --- | | `name` | chaîne | **oui** | Ce qu'affiche le menu Agent et le titre de la fenêtre. Doit être unique dans l'ensemble fusionné. | | `command` | chaîne | **oui** | L'exécutable à lancer. Cherché dans `PATH` sauf s'il contient un séparateur. | | `args` | liste de chaînes | non | Ses arguments, passés tels quels — pas de shell, donc ni guillemets, ni jokers, ni `&&`. | | `env` | table de chaînes | non | Variables d'environnement ajoutées à celles de l'éditeur. Un nom donné ici l'emporte. | | `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. | `env` peut aussi s'écrire en sous-table, ce qui est la même chose : ```toml [[agent]] name = "Bob (llama.cpp)" command = "docker" args = ["agent", "serve", "acp", ".turbo-go/agent.yaml"] [agent.env] TELEMETRY_ENABLED = "false" ``` ### Ce qui est refusé 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. | Problème | Message | | --- | --- | | un agent sans `name` | `reading …/acp.toml: agent 1 has no name` | | un agent sans `command` | `reading …/acp.toml: agent "Bob" has no command` | | deux agents portant le même `name` | `reading …/acp.toml: two agents are called "Bob"` | | une clé que le format ne définit pas | `reading …/acp.toml: agent.comand is not a key this file has` | Le dernier cas est voulu : une clé mal orthographiée silencieusement ignorée ressemblerait exactement à une clé sans effet. ## Le menu Agent `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. | Entrée | Active quand | Effet | | --- | --- | --- | | *une entrée par agent, par son nom* | toujours | Démarrer cet agent et ouvrir une fenêtre dessus | | **Create agents file** | pas d'`acp.toml` dans le projet | Écrire le fichier de départ et l'ouvrir | | **Cancel turn** | un tour est en cours dans la fenêtre de devant | `session/cancel` | | **Agent status** | toujours | Ce qui a été chargé, la ligne de commande de chacun, et ce qui a échoué | ## Touches dans une fenêtre agent Une fenêtre agent est une fenêtre ordinaire : `F6`, `Alt-1`…`Alt-9`, Tile, Maximise, `[x]` et `[■]` y fonctionnent tous. À l'intérieur : | Touche | Effet | | --- | --- | | `Entrée` | Envoyer la zone de saisie comme invite | | `Alt-Entrée` | Insérer un saut de ligne dans la zone de saisie | | `Tab` | Déplacer le focus entre la conversation et la zone de saisie | | `Ctrl-C`, `Ctrl-Ins` | Copier la sélection, ou le bloc sur lequel est le curseur | | `Échap` | Abandonner la sélection ; s'il n'y en a pas, annuler le tour en cours | | `Ctrl-W` | Fermer la fenêtre et arrêter l'agent | Avec la **zone de saisie** au premier plan : | Touche | Effet | | --- | --- | | `↑` `↓` `←` `→` `Début` `Fin` | Déplacer le curseur dans ce que vous tapez | | `Retour arrière` `Suppr` | L'éditer ; le retour arrière en début de ligne la joint à celle du dessus | | `/` en premier caractère | Ouvrir la liste des commandes de l'agent — voir [Commandes et mentions](#commandes-et-mentions) | | `@` | Ouvrir la liste des fichiers du projet, réduite par ce que vous tapez ensuite | | `↑` `↓` `PgUp` `PgDn`, liste ouverte | Se déplacer dans la liste | | `Tab`, liste ouverte | Prendre l'entrée en surbrillance | | `Entrée`, liste ouverte | Prendre l'entrée en surbrillance ; sur un mot déjà complet, envoyer | | `Échap`, liste ouverte | Fermer la liste jusqu'à ce que le texte change | Avec la **conversation** au premier plan : | Touche | Effet | | --- | --- | | `↑` `↓` | Déplacer le curseur d'une ligne | | `PgUp` `PgDn` | Le déplacer d'un écran | | `Début` `Fin` | Le début de la conversation, et la fin | | `Shift-` l'une d'elles | Étendre la sélection à la place | | Glisser avec le bouton 1 | Sélectionner à la main | | Molette | Défiler de trois lignes sans bouger le curseur | 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. ## Commandes et mentions 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. ### `/` — les commandes de l'agent 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. `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. 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. Sans commande annoncée, `/` est un caractère et `Tab` garde son sens habituel. **Agent ▸ Agent status** liste les commandes avec leur description. ### `@` — un fichier du projet 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. À 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** : | L'agent a déclaré | Le bloc envoyé | | --- | --- | | `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é | | 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 | 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. 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. 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. ## La copie 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é. 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. L'indentation d'affichage de la conversation est retirée, donc le code collé arrive collé à la marge. Le texte part à deux endroits à la fois : | Presse-papiers | Comment | Collé avec | | --- | --- | --- | | Celui de l'éditeur | directement | `Shift-Ins`, dans un fichier ouvert ici | | Celui du système | OSC 52, à travers le terminal | `Ctrl-V`, n'importe où ailleurs | 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. ## Quelle part du protocole est implémentée Version de protocole **1**. Turbo Go annonce sa version dans `initialize` et accepte la version que l'agent répond, pourvu qu'il la connaisse. ### Ce que l'éditeur appelle sur l'agent | Méthode | Implémentée | Remarques | | --- | --- | --- | | `initialize` | oui | Annonce la capacité `fs` ci-dessous ; `terminal` n'est pas annoncée | | `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 | | `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) | | `session/cancel` | oui | `Échap`, et **Agent ▸ Cancel turn** | | `session/load` | **non** | Les conversations ne survivent pas à la fermeture de la fenêtre | | `authenticate` | **non** | Un agent qui liste des `authMethods` est signalé comme exigeant une connexion que l'éditeur ne sait pas faire | ### Ce que l'agent peut appeler sur l'éditeur | Méthode | Implémentée | Remarques | | --- | --- | --- | | `session/update` | oui | Voir la table ci-dessous | | `session/request_permission` | oui | Une boîte modale portant les options de l'agent lui-même | | `fs/read_text_file` | oui | Depuis le tampon quand le fichier est ouvert et modifié, sinon depuis le disque | | `fs/write_text_file` | oui | Dans le tampon quand le fichier est ouvert, sinon sur le disque | | `terminal/*` | **non** | Non annoncée, donc un agent conforme ne la demandera pas | ### Mises à jour de session | `sessionUpdate` | Affiché comme | | --- | --- | | `agent_message_chunk` | La réponse de l'agent, ajoutée au fil de son arrivée | | `agent_thought_chunk` | La même chose, dans la couleur des commentaires, sous une étiquette *réflexion* | | `user_message_chunk` | Votre propre message, tel que l'agent le renvoie | | `tool_call` | Une ligne nommant l'outil et son titre, avec son état | | `tool_call_update` | Repliée sur la ligne dont le `toolCallId` correspond, avec sa sortie | | `plan` | Les entrées en liste, chacune avec son état | | `available_commands_update` | La liste que `/` ouvre dans la zone de saisie ; aussi listée, avec les descriptions, par **Agent ▸ Agent status** | | `usage_update` | Le compte de jetons dans la barre d'état quand la fenêtre est devant | | tout le reste | Ignoré, et compté ; le compte figure dans **Agent status** | 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. 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. ## Coloration 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é : | Élément | Classe | | --- | --- | | Le nom d'un interlocuteur | `syntax.keyword` | | Une réflexion | `syntax.comment` | | Un appel d'outil et son état | `syntax.type` | | Un appel d'outil en échec, et les avis de l'éditeur | `diagnostic.error` | | Une ligne sélectionnée, et la barre du curseur | `editor.selection` | | Le code dans un bloc délimité | l'analyseur du langage annoncé | | Tout le reste | le texte ordinaire de la fenêtre | Un bloc délimité annonçant un langage que l'éditeur colore — `go`, `toml`, `yaml`, `markdown`, `javascript`, `html`, `xml`, `dockerfile`, `bash` — est coloré par cet analyseur. Un bloc annonçant autre chose, ou rien, est laissé brut. ## Tracer la conversation avec un agent | Variable | Effet | | --- | --- | | `TURBO_ACP_TRACE=` | Ajouter à ce fichier chaque message vers et depuis chaque agent, un par ligne, horodaté et marqué `->` (envoyé) ou `<-` (reçu) | 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. ## Limites - **Une session par fenêtre.** Fermer la fenêtre termine la session ; il n'y a pas de reprise. - **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. - **Pas d'authentification.** Un agent exigeant une connexion doit être connecté par sa propre CLI avant que l'éditeur ne le lance. - **`args` n'est pas une commande shell.** `command = "sh"`, `args = ["-c", "…"]` est la façon délibérée d'en obtenir une. - **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. ## Voir aussi - La tâche : [Dialoguer avec un agent de code depuis l'éditeur](../how-to/talk-to-an-agent.md) - Le raisonnement : [Fenêtres agent](../explanation/agent-windows.md) - Le protocole : [agentclientprotocol.com](https://agentclientprotocol.com)