Référence : surface Agent Client Protocol
Description neutre de ce que
mm -acpexpose en JSON-RPC 2.0 sur stdio, viagithub.com/coder/acp-go-sdkv0.13.5.
Transport
| Aspect | Valeur |
|---|---|
| Entrée | stdin, un message JSON-RPC par ligne. |
| Sortie | stdout, messages JSON-RPC uniquement. |
| Journaux | stderr : bannière, avertissements, lignes [acp] …, logger du SDK. |
| Durée de vie | Jusqu'à ce que le client ferme stdin ou que le contexte du processus soit annulé. |
Réponse à initialize
| Champ | Valeur |
|---|---|
protocolVersion |
1 |
agentCapabilities |
loadSession: true quand sessions.enabled est activé, absente (false) sinon. Tout le reste absent (false) : pas de prompts image ou audio, pas de MCP HTTP. |
agentInfo.name |
acp.name de la configuration, bob par défaut. |
agentInfo.title |
acp.title de la configuration, Bob (bash-first agent) par défaut ; absent quand vide. |
agentInfo.version |
La version du binaire, la même chaîne que mm --version (par exemple v0.1.7). |
authMethods |
[] |
Méthodes
| Méthode | Comportement |
|---|---|
initialize |
Journalise le nom du client et la version du protocole sur stderr ; renvoie les valeurs ci-dessus. |
authenticate |
Renvoie une réponse vide. |
session/new |
Crée une session avec un identifiant triable par date (20260916-101530-4f2a9c1e) et le cwd de la requête, un historique neuf contenant le prompt système et un ensemble « toujours autoriser » vide. Les mcpServers sont comptés dans le journal et ignorés. Une fois la réponse écrite, envoie une notification available_commands_update listant les commandes slash (voir plus bas). Avec sessions.enabled, écrit <cwd>/<sessions.dir>/<id>.json immédiatement et le réécrit après chaque tour, /new et /compact. |
session/prompt |
Exécute une génération sur l'historique de la session, ou exécute une commande slash sans consulter le modèle. Les tours sont sérialisés à l'échelle du processus. |
session/cancel |
Annule le tour en cours pour cette session. |
session/load |
Avec sessions.enabled : lit <cwd>/<sessions.dir>/<sessionId>.json, remplace son prompt système par l'actuel, enregistre la session (ensemble « toujours autoriser » vide), envoie le rejeu (ci-dessous) puis un available_commands_update, et répond {}. Identifiant inconnu ou fichier illisible : invalid params portant le sessionId et l'erreur. Sans sessions.enabled : method not found. |
session/set_mode, session/set_config_option, session/list, session/resume, session/close, logout |
method not found. |
Les skills comme commandes
ACP n'a aucune notion de skill. Un prompt porte du text, une image, de l'audio, une resource ou un resource_link — ce dernier étant ce qu'envoie le sélecteur @ d'un éditeur — et rien pour « exécute cette procédure ».
AvailableCommand est le bon créneau, et les exemples du protocole le disent : sa documentation propose create_plan et research_codebase, qui sont des procédures et non des commandes de session. Chaque skill du catalogue y est donc annoncé, et l'éditeur le liste dans son propre menu / :
/compact Compress the history now, keeping the last turns
/new Clear the history and start a new session
/bg-jobs [input: parameters, if …] Start, wait on, inspect and stop a long-running background job…
/greetings [input: parameters, if …] Greet somebody with a festive message.
/vulcan-salute [input: parameters, if …] Send a vulcan salute to somebody.
C'est l'input.hint qui porte les paramètres : le protocole dit « tout le texte tapé après le nom de la commande est fourni en entrée », ce qui est exactement ce qu'attend un skill.
| Aspect | Comportement |
|---|---|
Ce que fait /greetings Bob |
Pas intercepté. Réécrit en la notation $greetings Bob, et le tour se déroule normalement : c'est toujours le modèle qui fait l'appel read_skill, donc le détecteur de boucle, la demande de permission et le recap 📖 N skill(s) le voient tous, et mm ne charge jamais un skill dans le dos du modèle. |
| Une vraie commande gagne | session/prompt tente l'interception d'abord, donc un skill nommé compact ou new ne peut jamais en masquer une. Un tel skill n'est pas annoncé non plus, et la bannière de démarrage le signale — sinon il serait silencieusement inatteignable depuis le menu. |
Un /nom inconnu |
Laissé tel quel et passé au modèle, comme n'importe quel texte slash arbitraire avant. |
Taper $greetings Bob à la main |
Marche aussi, dans l'éditeur comme dans le terminal : la notation est résolue dans session/prompt, que le menu l'ait écrite ou l'utilisateur. |
À noter, l'invariant a changé avec ça : c'était « n'annoncer que ce que session/prompt intercepte », c'est maintenant « n'annoncer que ce que cette façade traite » — réécrire, c'est traiter.
Rejeu de session/load
Envoyé avant la réponse, une notification par élément de l'historique enregistré, dans l'ordre. Le prompt système n'est pas envoyé.
| Élément enregistré | Notification |
|---|---|
| Message utilisateur | user_message_chunk avec son texte. |
Résumé de compression (message de rôle utilisateur marqué par /compact) |
agent_message_chunk avec 🗜️ suivi du résumé. |
| Texte du modèle | agent_message_chunk. |
| Appel d'outil avec sa réponse enregistrée | tool_call avec toolCallId = la référence de l'appel, title et kind tels que l'outil en direct les donnerait (bash: <commande> execute, read_file: <chemin> read, write_file/edit_file edit, read_skill: <nom> read), status: completed, rawInput, rawOutput.output et un contenu text portant la sortie. |
| Appel d'outil sans réponse enregistrée | tool_call avec status: failed et sans contenu. |
Aucun outil ne s'exécute pendant un rejeu. Journalisé comme [acp] session <id> loaded from <dir>: N message(s) replayed.
session/prompt
| Aspect | Comportement |
|---|---|
sessionId inconnu |
Erreur invalid params portant le sessionId. |
| Contenu du prompt | Blocs text concaténés ; un resource_link devient \n[attached file: <chemin sans file://>] ; les autres types de blocs sont ignorés. Ensuite, chaque @chemin du texte qui désigne un fichier ou un répertoire existant sous le cwd de la session (ou un chemin absolu / ~) ajoute \n[attached file: <chemin absolu>] ou \n[attached directory: …], une fois par chemin, et est journalisé comme [acp] session <id>: attached <chemin>. |
| Commande slash | Quand le texte aplati, débarrassé de ses espaces, est exactement /new ou /compact, aucune génération n'a lieu ; la commande répond par un agent_message_chunk et stopReason: end_turn. |
/new |
L'historique de la session revient au seul prompt système, le dernier compte de tokens d'entrée du moteur est oublié, le fragment est 🆕 New session: N message(s) forgotten.. L'identifiant de session, le cwd et les autorisations « toujours autoriser » sont conservés. Avec sessions.enabled, la conversation écoulée est d'abord enregistrée sous un nouvel identifiant (journalisé previous conversation kept as <id> ; rien n'est enregistré si elle était vide) et le fichier de la session est réécrit avec l'historique neuf. Journalisé comme [acp] session <id>: new session, N message(s) forgotten. |
/compact |
L'historique est compressé avec les réglages context, en mode forcé (le seuil est ignoré, keepLastTurns est respecté). Le fragment est 🗜️ compressed N messages → 1 summary + N kept (… tokens, …s), ou 🗜️ nothing to compact: N message(s), no turn older than the last K (historique inchangé), ou [compact: failed, history kept: <explication>] (historique inchangé). Le compte de tokens n'est oublié que si l'historique a changé. Attend la fin d'un tour en cours : les tours sont sérialisés. Journalisé comme [acp] session <id>: /compact — <ligne>. |
| Historique | L'historique complet renvoyé par le moteur est conservé, même après un échec ou une annulation ; une question restée sans aucune réponse est retirée. |
| Succès | stopReason: end_turn. |
| Annulé | stopReason: cancelled. |
| Échec | internal error avec error: <explication en une ligne>. |
| Ligne de journal | [acp] turn done: N command(s) · N file op(s) · N skill(s) sur stderr. |
Notifications session/update envoyées
| Mise à jour | Quand |
|---|---|
available_commands_update |
Une fois par session, juste après l'écriture de la réponse à session/new (l'éditeur doit connaître l'id de session d'abord, sinon il ignore la notification). Liste ce que cette façade traite : les commandes slash que session/prompt intercepte, puis une commande par skill. Envoyée aussi à la fin d'un rejeu session/load. |
agent_message_chunk (texte) |
Chaque fragment diffusé de la réponse du modèle, espaces préservés ; aussi la ligne de confirmation d'une commande slash. |
tool_call statut pending |
Avant l'exécution d'un outil ; porte title, kind, rawInput, et locations avec le chemin absolu quand un fichier est concerné. |
tool_call_update statut in_progress |
Permission accordée, outil en cours d'exécution. |
tool_call_update statut completed ou failed |
Outil terminé ; content est un diff pour les changements de fichier, sinon un bloc text avec la sortie ; rawOutput.output porte toujours la sortie. |
Commandes disponibles
name |
description |
input |
|---|---|---|
new |
Clear the history and start a new session |
aucun |
compact |
Compress the history now, keeping the last turns |
aucun |
| un par skill | la description du skill |
{ "hint": "parameters, if this skill needs any" } |
Les clients affichent le nom précédé d'une barre oblique et le renvoient comme texte d'un session/prompt. Les deux commandes ci-dessus y sont interceptées ; la commande d'un skill est réécrite en la notation $skill et le tour se déroule — voir Les skills comme commandes.
Requêtes session/request_permission envoyées
Envoyées avant l'exécution de bash, read_file, write_file et edit_file ; jamais pour read_skill.
| Identifiant d'option | Genre | Effet |
|---|---|---|
allow |
allow_once |
Exécute l'outil. |
allow_always |
allow_always |
Exécute l'outil et supprime le dialogue pour ce nom d'outil jusqu'à la fin de la session. |
reject |
reject_once |
N'exécute pas ; le modèle reçoit [command rejected by the user]. |
Une erreur de transport ou un résultat cancelled compte comme un refus.
Genres d'outils
Genre mm |
ToolKind ACP |
Outils |
|---|---|---|
execute |
execute |
bash (et tout genre inconnu) |
read |
read |
read_skill, read_file |
edit |
edit |
write_file, edit_file |
Identifiants d'appel d'outil
call_1, call_2, … numérotés par processus et jamais remis à zéro.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
|