Exposer un agent ADK-Rust via ACP

Utilisez l’orientation serveur lorsqu’un éditeur ou un autre client ACP doit démarrer votre binaire ADK-Rust et utiliser son agent dans une interface de codage. Votre processus Rust gère l’agent, le modèle, les outils, les flux de travail, les sessions, la mémoire et la politique d’exploitation. Le client ne voit que les capacités et le cycle de vie des sessions publiés via ACP.

Installer la fonctionnalité serveur

[dependencies]
adk-acp = { version = "2.1.0", features = ["server"] }

Construire et servir un agent

use adk_acp::server::{AcpServer, AcpServerConfigBuilder};
use adk_session::InMemorySessionService;
use std::sync::Arc;

let config = AcpServerConfigBuilder::new()
    .agent(Arc::new(repository_agent))
    .session_service(Arc::new(InMemorySessionService::new()))
    .agent_name("repository-guide")
    .agent_description("Explains and improves this Rust workspace")
    .max_sessions(16)
    .build()?;

let handle = AcpServer::run(config).await?;
handle.wait().await?;

Le serveur utilise le générateur officiel Agent de SDK et le transport stdio. Le trafic du protocole est la seule donnée écrite sur stdout ; configurez la journalisation et les diagnostics pour utiliser stderr.

Correspondance d’exécution

Rendering architecture…

Le gestionnaire valide un cwd absolu, réserve la capacité de session, crée ou reprend la session ADK et exécute l’agent configuré. Les événements ADK typés sont traduits en notifications session/update ACP pendant que l’invite est active.

Cycle de vie implémenté

Opération ACPComportement ADK-Rust
initializeNégocie le protocole v1 et renvoie les métadonnées exactes de l’implémentation et des capacités
session/newValide les chemins de l’espace de travail et crée une session ADK persistante
session/promptConvertit les blocs de contenu pris en charge (text, resource-link, embedded-resource, image, audio) et diffuse le Runner
session/loadRéactive une session persistée (en validant cwd) et rejoue sa conversation enregistrée sous forme de notifications session/update ordonnées avant de terminer
session/cancelAnnule l’invocation active du Runner et renvoie une raison d’arrêt indiquant l’annulation
$/cancel_requestAnnule la requête JSON-RPC correspondante sans corrompre la session
session/closeAnnule le travail actif et libère les processus appartenant à la session
session/listRépertorie les sessions persistées visibles par ACP
session/resumeSe rattache à la session et à l’espace de travail d’origine
session/forkCrée une branche d’une session persistée dans une nouvelle session, en copiant son historique et l’état pertinent, tout en laissant la source inchangée
session/set_modeValide et enregistre un mode de session déclaré par le SessionControls de l’agent, en émettant un CurrentModeUpdate
session/set_config_optionValide et enregistre une valeur de configuration déclarée par le SessionControls de l’agent, en émettant un ConfigOptionUpdate
session/deleteSupprime l’historique persistant et libère les ressources actives

Une seule invite peut s’exécuter à la fois dans une session. Différentes sessions peuvent s’exécuter simultanément jusqu’à max_sessions.

Mappage des événements

  • le texte du modèle devient agent_message_chunk ;
  • le contenu de réflexion du modèle devient agent_thought_chunk ;
  • le contenu de la ressource intégrée devient une ressource intégrée agent_message_chunk de ACP ;
  • les appels de fonction ADK deviennent des mises à jour de démarrage d’outil ACP avec un outil kind déduit ;
  • les réponses de fonction deviennent des mises à jour d’achèvement de l’outil enrichies du contenu du résultat et de tout emplacement de fichier affecté, associées à l’appel d’outil d’origine ;
  • les événements contenant des métadonnées d’utilisation deviennent des notifications UsageUpdate (nombre de jetons, ainsi que le coût en USD lorsqu’il est communiqué) ;
  • les commandes déclarées par l’agent deviennent un AvailableCommandsUpdate lorsqu’une session devient active, et un titre de session enregistré devient un SessionInfoUpdate ;
  • les entrées du plan deviendraient une mise à jour Plan — ce mappage existe, mais reste inactif jusqu’à ce qu’une primitive de plan ADK expose les entrées du plan ;
  • l’annulation devient StopReason::Cancelled ;
  • l’achèvement normal devient StopReason::EndTurn.

Un module de contenu partagé gère la correspondance ContentBlockadk_core::Part dans les deux sens. Le contenu d’invite d’une ressource intégrée est converti en Part::EmbeddedResource, en préservant la source URI, le type MIME facultatif et le contenu ; les ressources textuelles sont préservées à l’identique, tandis que les ressources binaires sont encodées en base64 sur le réseau, puis décodées en octets bruts en interne. Le contenu d’invite d’image et audio est converti en Part::InlineData, en préservant le type MIME, les octets décodés, les annotations et la source URI facultative d’une image. Ces champs restent dans la JSON de session et sont restaurés par session/load. Comme le gestionnaire d’invites accepte le contenu de ressources intégrées, d’images et audio, le serveur annonce les capacités d’invite embedded_context, image et audio. Une invite contenant un type de contenu que le serveur n’a pas annoncé est rejetée avec une erreur explicite au lieu d’être traitée partiellement.

Chargement et relecture de l’historique

session/load restaure l’historique visible d’une session persistante lorsqu’un client se reconnecte. Le gestionnaire réactive la session de la même manière que session/resume — en vérifiant que l’appelant a fourni le cwd d’origine et en renvoyant une erreur indiquant que la session est introuvable pour un identifiant inconnu — puis effectue une passe de relecture. Il lit les événements persistants via le service de session et convertit chaque événement stocké d’utilisateur, d’agent, de réflexion et d’outil en sa notification session/update correspondante, dans l’ordre chronologique d’origine, avant la fin de la requête de chargement. Le serveur annonce la capacité load_session afin qu’un client sache qu’il peut se reconnecter et reconstruire la vue de la conversation.

Modes de session, options de configuration et bifurcation

Un agent active les contrôles de session interactive en fournissant un fournisseur SessionControls via AcpServerConfigBuilder::session_controls. Le fournisseur déclare les modes disponibles (un SessionModeState), les options de configuration (sélections et boutons bascule), ainsi que les commandes slash ACP. Le serveur annonce exactement ce que le fournisseur déclare — un agent sans fournisseur n’annonce aucun mode ni aucune option — et les expose dans les réponses session/new, session/load, session/resume et session/fork.

session/set_mode valide l’identifiant du mode demandé par rapport à l’ensemble annoncé, l’enregistre et émet un CurrentModeUpdate ; un identifiant inconnu est rejeté et le mode actuel reste inchangé. session/set_config_option valide la valeur par rapport aux choix déclarés pour l’option, l’enregistre et émet un ConfigOptionUpdate ; une option inconnue ou une valeur non valide est rejetée. Les deux sélections sont conservées dans l’état de session ADK sous acp:mode et acp:config:<id>, et persistent donc lors du chargement, de la reprise et de la création d’une branche.

session/fork crée une branche d’une session persistante : il lit la session source, crée un nouvel identifiant de session, y copie les événements enregistrés et l’état pertinent (cwd, les répertoires supplémentaires, le mode et la configuration), puis renvoie le nouvel identifiant. L’historique persistant de la session source reste inchangé octet par octet. La création d’une branche pour un identifiant de session inconnu renvoie une erreur indiquant que la session est introuvable. Le serveur annonce la capacité de session fork, car le gestionnaire est enregistré.

Lors de l’activation d’une session, le serveur émet également un AvailableCommandsUpdate pour toutes les commandes déclarées par le fournisseur (et aucun lorsqu’il n’en déclare aucune), ainsi qu’un SessionInfoUpdate contenant le titre de la session lorsqu’un titre est enregistré sous acp:title (défini via set_session_title). Un mappage de mise à jour Plan existe, mais reste inactif jusqu’à ce qu’une primitive de plan ADK expose des entrées de plan.

Serveurs MCP fournis par le client

Le client peut inclure des serveurs stdio MCP dans session/new ou session/resume. Le serveur valide les noms, les commandes, les arguments et les entrées d’environnement avant de démarrer un processus. Il :

  1. démarre chaque processus enfant dans l’espace de travail de la session ;
  2. applique une négociation de démarrage limitée ;
  3. encapsule la connexion en tant que ADK McpToolset ;
  4. injecte l’ensemble d’outils dans cette invocation de Runner ;
  5. annule les services MCP lors de la fermeture, de la suppression, d’un échec du démarrage ou de l’arrêt du serveur.

Les ensembles d’outils associés à une invocation sont actuellement résolus par LlmAgent et CodeActAgent. Les transports HTTP et SSE MCP facultatifs ne sont pas annoncés par le serveur.

Décisions de persistance

InMemorySessionService convient à un processus d’éditeur local et aux tests. Utilisez un service durable lorsque les sessions doivent survivre aux redémarrages du processus. La reprise valide que l’appelant fournit le cwd d’origine ; une session ne peut pas être rattachée silencieusement à un autre projet.

Limite d’approbation des outils

Le serveur fait le lien entre les confirmations d’outils ADK et les demandes d’autorisation natives ACP. Lorsque l’agent configuré se met en pause sur un ToolConfirmationRequest pendant un tour de requête — signalé sur event.actions.tool_confirmation lorsqu’un agent attend l’approbation humaine d’un appel d’outil — le serveur envoie une requête session/request_permission décrivant l’outil et ses arguments, attend le résultat du client, puis reprend l’exécution avec la décision correspondante. Une approbation est mappée vers l’autorisation, et un refus ou une annulation sont tous deux mappés vers le refus, de sorte qu’une requête annulée n’exécute jamais l’outil. Chaque résultat est associé à l’appel exact au moyen de son identifiant d’appel de fonction et renvoyé au Runner via RunConfig::tool_confirmation_decisions.

Le session/request_permission imbriqué est émis depuis la tâche qui gère déjà le session/prompt externe, généré via ConnectionTo::spawn ; il ne bloque donc pas la boucle de distribution de la connexion, et la réponse à l’invite externe s’achève tout de même. Une inquiétude précédente selon laquelle le SDK Rust officiel perdrait la réponse à l’invite externe après une requête bidirectionnelle imbriquée ne se reproduit pas avec ce flux de suspension/reprise ; ce cas est couvert par les tests d’interopérabilité en mémoire.

L’autorisation des outils gérée par le serveur, les outils en lecture seule, RBAC, les garde-fous et les interruptions de workflow restent disponibles lorsque l’approbation doit avoir lieu entièrement au sein du processus ADK-Rust. Le chemin d’autorisation côté client pour les agents ACP externes est également entièrement implémenté.

Déployer en toute sécurité

  • Démarrez le binaire avec l’espace de travail du projet prévu.
  • Considérez cwd et les racines supplémentaires comme un contexte, et non comme un mécanisme d’isolation du système d’exploitation.
  • Appliquez adk-sandbox, un conteneur ou une autre limite de processus pour les invites et commandes non fiables.
  • Conservez les identifiants du modèle et de MCP dans un magasin de secrets client ou dans l’environnement du processus.
  • N’écrivez jamais de bannières, d’objets de débogage ou de journaux sur la sortie standard du protocole.
  • Utilisez un SessionService durable lorsque la reprise doit survivre au redémarrage du processus.
  • Définissez une limite de session finie et fermez les sessions inactives.

Le crate acp_server exécutable inclut un agent basé sur Gemini, des outils de lecture limités à l’espace de travail, la journalisation de suivi vers la sortie d’erreur et la configuration du processus de l’éditeur.

Ensuite