Protocole ACP
Responsabilité
ACP (Agent Client Protocol) est un protocole standard de communication entre un éditeur / IDE et un agent. acp_adapter/ expose Hermes comme serveur ACP, afin que les clients compatibles ACP (Zed, extension VS Code, etc.) puissent piloter Hermes directement : gérer les sessions (sessions), lire/écrire des ressources (fichiers/images), approuver des modifications, tracer la provenance, isoler les outils (tools) selon les permissions. Hermes n'est plus seulement une plateforme de messagerie, il peut aussi s'imbriquer dans un flux de développement.
Mot de conception
Hermes a été conçu à l'origine pour les plateformes de messagerie — la couche passerelle (gateway) branche Telegram / Discord, des conversations longues. Mais un IDE est autre chose : le répertoire de travail est ouvert par l'éditeur, le cwd doit suivre l'éditeur ; modifier un fichier ne doit pas se faire au seul motif que l'agent le dit, il faut le remonter à l'utilisateur pour validation ; et il faut pouvoir retracer « celle-ci est compressée à partir de laquelle ». La couche ACP traduit ces contraintes spécifiques à l'IDE en primitives qu'Hermes possède déjà. Le code ne réécrit pas une boucle d'agent : il branche le flux de messages ACP sur run_conversation, convertit les ressources ACP en OpenAI content parts, et transforme les opérations d'écriture de fichier issues des appels d'outils en EditProposal soumis à validation humaine. Le cœur est « adapter, pas réécrire ».
Fichiers clés
en-tête server.py:1-92— « ACP agent server — exposes Hermes Agent via the Agent Client Protocol »traitement des ressources:96-150—_MAX_ACP_RESOURCE_BYTES,_resource_display_name,_is_text_resource/_is_image_resourceconversion ressource texte/image:201-296—_format_resource_text/_resource_link_to_parts/_image_data_url(ressource → parties de contenu OpenAI)SessionState / SessionManager:159-175— état et gestionnaire de session ACPtraduction cwd:29-62—_translate_acp_cwd/_normalize_cwd_for_compare(alignement du répertoire de travail de l'éditeur)expansion toolsets:129-159—_expand_acp_enabled_toolsetsclass EditProposal:26-51— proposition d'édition (fichier / patch)build_edit_proposal:178-220— construit la proposition depuis un appel d'outil (write_file / patch_replace / patch_v4a)demandeur d'approbation:51-69—set_edit_approval_requester/ mécanisme de tokenpermissions.py— isolation des permissions d'outilsprovenance:22-111—build_session_provenance/session_provenance_meta(traçabilité de la source des opérations)auth.py/entry.py/events.py__main__.py— entrée de démarrage du serveur ACP (5 lignes)acp_registry/agent.json— manifeste de métadonnées de l'agent (pour découverte par les clients)acp_registry/icon.svg— icône
Flux de données
- Le client ACP (éditeur) découvre et démarre le serveur ACP d'Hermes via
acp_registry/agent.json(acp_adapter/__main__.py). - Le client ouvre une session →
SessionManager:175crée unSessionState, aligne le cwd (acp_adapter/session.py:29), déploie les toolsets (acp_adapter/session.py:129). - Le client envoie messages / ressources →
conversion de ressource:201transforme la ressource ACP (fichier/image, sous la limiteacp_adapter/server.py:96) en parties de contenu OpenAI, et alimenterun_conversation:588. - Quand l'agent veut modifier un fichier, l'appel d'outil devient un
EditProposalviabuild_edit_proposal:178, soumis à l'utilisateur via le demandeur d'approbation (acp_adapter/edit_approval.py:51). - L'exécution des outils est contrôlée par les permissions
acp_adapter/permissions.py; chaque opération est tracée paracp_adapter/provenance.py:22. - Le streaming et les événements (
acp_adapter/events.py) sont renvoyés au client.
acp_registry/agent.json est la carte de visite du client la première fois qu'il rencontre Hermes : version, mode de distribution (uvx), commande de démarrage. Ce contrat doit rester aligné avec le binaire réel, sinon le client installe une mauvaise version et ne peut pas démarrer.
La traduction cwd est l'endroit où le scénario ACP piège le plus : un client Windows fait tourner Hermes dans WSL, mais son espace de travail est E:\Projects ou un chemin UNC \\wsl.localhost\.... _translate_acp_cwd convertit tout en POSIX, sinon toutes les opérations Path côté agent sont décalées.
La proposition d'édition est une frozen dataclass, la clé est old_text — _proposal_for_write_file lit d'abord le contenu existant du fichier, pour que le diff affiche « avant / après » plutôt qu'une modification aveugle :
@dataclass(frozen=True)
class EditProposal:
tool_name: str
path: str
old_text: str | None
new_text: str
arguments: dict[str, Any]Le demandeur d'approbation est lié via un ContextVar, pas une variable globale : plusieurs sessions ACP concurrentes ont chacune leur requester, sans fuite entre elles. .env / id_rsa passent par défaut par ask plutôt que workspace_session, pour ne pas être relâchés par une politique « tout le workspace est de confiance ».
La provenance est spécifique au scénario IDE : en messagerie la session est linéaire, mais en IDE elle est compressée, bifurquée, reprise. build_session_provenance remonte parent_session_id, compte le nombre de niveaux où end_reason == 'compression', marque sessionKind et compressionDepth, et place l'id de session Hermes interne et l'id de session ACP externe ensemble dans _meta.hermes.sessionProvenance.
Limites et échecs
- Décalage de version au handshake :
agent.jsonépinglehermes-agent[acp]==0.19.0. Quand Hermes passe à 0.20 et que le client a encore l'ancien manifeste en cache, le serveur démarré et le schéma client ne collent plus ; si InitializeResponse manque un champ, le handshake échoue. - Traduction WSL cwd qui passe au travers : quand
translate_cwd_for_wsl_backendne couvre pas un nouveau format de chemin Windows, l'agent reçoit un cwd décalé, toutes les opérationsPathpartent de travers — sans erreur immédiate, ça n'explose qu'à la première écriture avec « répertoire introuvable ». - Deadlock du demandeur d'approbation : l'approbation passe par
ContextVar+ callback utilisateur ; si la event loop du client bloque et ne renvoie pas l'ACK, l'attente aprèsbuild_edit_proposalreste suspendue. Que le cancel de la couche ACP se propage jusqu'à ce point d'attente dépend de la couverture du chemin d'interruption dansevents.py. - Chaîne de provenance cassée : lors d'une bifurcation par compression (compression), si
parent_session_idest mal écrit,build_session_provenancetombe sur un point de rupture et renvoieroot— le client présente alors une continuation comme une nouvelle session, et l'historique paraît « perdu ».
Résumé
ACP permet à Hermes de s'imbriquer dans le flux d'un éditeur : gestion de session, lecture/écriture de ressources, approbation des modifications, isolation des permissions, traçabilité de la provenance. Le cœur est server.py (adaptation de protocole) + session.py (session) + edit_approval.py (édition approuvée par un humain). Complémentaire de la couche passerelle — la passerelle branche les plateformes de messagerie, ACP branche l'IDE.