Aller au contenu

Cycle de vie du lancement d'un agent

Un lancement correspond à une exécution d’un agent, dans une conversation, sur un adaptateur. Tout se déroule sur cc_server : le client envoie un message puis regarde les événements affluer en retour. Aucun client ne comporte de chemin de lancement.

Les agents sont réveillés pour six raisons, que l’exécution transporte sous forme de contexte de réveil, afin que l’agent sache pourquoi il est réveillé :

Motif de réveil Déclencheur
userMessage Quelqu’un a envoyé un message dans un espace ou a mentionné l’agent
assignment Un ticket a été assigné à l’agent
pipelineStep Une étape de pipeline l’a lancé
childCompleted Un ticket enfant s’est terminé
followUp Un battement de relance planifié
recovery Le chemin de récupération a relancé après un échec

Le contexte de réveil transporte les identifiants associés au motif — ticket, espace, message, exécution de pipeline — et ceux-ci sont injectés dans l’environnement et le prompt de l’agent.

Le prompt est construit par couches, chacune étant une section étiquetée :

  • Identité — l’identifiant, le nom et l’identifiant d’espace de travail de l’agent lui-même, avec l’instruction explicite de transmettre ces UUID exacts aux outils limités à l’espace de travail. L’espace de travail est intrinsèque à l’agent, il est donc toujours présent.
  • Catalogue d’outils et protocoles de ressources, puis la disposition de l’espace de travail.
  • Remplacement du prompt système, si l’agent en définit un, puis la persona (paramétrée par le rôle) et le brief d’équipe.
  • Skills — les noms attachés à l’agent.
  • Contrat d’exécution et procédure pour le mode en cours.
  • Contexte de réveil et contexte de mention : qui a convoqué l’agent et l’effectif qui l’entoure.
  • Bloc de mode — généré à partir du profil de capacités du mode et de sa liste d’outils matérialisée, afin qu’un prompt ne puisse jamais nommer un outil que l’exécution ne possède pas réellement.
  • Contexte de mémoire — les faits récupérés pour la tâche.
  • Contexte de conversation — l’historique récent de l’espace, plafonné à 50 000 caractères de fenêtre verbatim, quelle que soit la taille de contexte configurée pour l’agent. L’historique plus ancien n’est pas tronqué : l’agent le récupère à la demande avec get_messages et les messages plus anciens pertinents sur le plan sémantique refont surface.

Les commandes se répartissent sur deux endroits. /todo et /compact sont interceptées dans le compositeur et ne sont délibérément jamais enregistrées comme messages ni envoyées à l’agent — enregistrer /compact repousserait la coupure de compactage d’un tour et polluerait la transcription. /plan est traitée aux deux endroits : le compositeur bascule le mode de l’espace, ce qui fait que la surface d’outils, le préréglage de garde-fous et le prompt s’accordent pour ce tour et pour tous les tours suivants. /goal, /loop et /skill:<name> sont traitées à l’intérieur de l’exécution elle-même et uniquement sur le harnais intégré — /goal et /loop lancent un travail autonome avec un budget de coût, et un nom préfixé par skill: est recherché comme un skill dont le corps du SKILL.md devient la directive du tour. L’espace de noms est ce qui sépare les deux vocabulaires : résoudre un nom brut contre les skills signifiait qu’un skill nommé plan ou compact était définitivement masqué par celui intégré. Un nom brut non intégré est toujours résolu, pour les messages écrits avant l’existence de l’espace de noms. Un nom non reconnu passe simplement comme texte brut.

Avant que quoi que ce soit ne s’exécute, la conversation obtient sa propre racine de travail, avec sous celle-ci un arbre de travail isolé en copie sur écriture par dépôt. Par défaut, il s’agit de chaque dépôt lié à l’espace de travail ; un espace de PR — ou un espace créé avec une sélection explicite — ne provisionne que ceux de la liste autorisée, et une conversation de revue de PR épingle son unique dépôt sur la référence de tête de la demande de tirage tandis que les autres restent sur leur branche par défaut. Le checkout source n’est jamais modifié et tout le répertoire de la conversation est récupéré lorsque l’espace est supprimé.

La copie sur écriture est la seule implémentation partout où rift est livré, adossée au natif embarqué. Un système de fichiers qui ne sait pas faire de reflink — ou un répertoire de données situé sur un volume différent de celui du dépôt — fait échouer le provisionnement au lieu de se rabattre sur une solution de repli. git worktree est le backend sous Windows, où il n’y a aucun backend de copie sur écriture à compiler, et ce n’est pas une dégradation silencieuse sur les autres hôtes. Un rift manquant sur une plateforme qui le livre est une installation cassée — le serveur refuse de démarrer plutôt que de se rabattre discrètement, car un repli silencieux dissimule la panne derrière un chemin plus lent pour toujours.

Deux identités sont résolues par exécution, avant le lancement du moindre processus.

Paternité Git. Les commits créés par un agent sont signés au nom de l’agent — <Name> (agent) <agentId@agents.control-center.local> à la fois dans GIT_AUTHOR_* et GIT_COMMITTER_* — sans jamais usurper l’identité d’un humain. La partie locale est l’UUID de l’agent, pas son slug de nom. L’humain à l’origine de la demande est crédité par une ligne Co-Authored-By:, portée dans l’environnement et rappelée dans le prompt système. Une identité définie explicitement par l’appelant l’emporte toujours sur les deux.

Identité de forge. L’identifiant GitHub de l’exécution n’est jamais que celui du courtier — un jeton d’installation d’App limité au dépôt, ou son repli sur un PAT d’environnement — jamais le jeton personnel stocké du membre à l’origine de la demande. actingUserId borne le repli sur le PAT afin qu’un membre qui n’est pas le propriétaire du serveur n’hérite pas de la portée du serveur ; il ne superpose pas un jeton personnel sur l’environnement de l’agent.

Les identifiants du fournisseur de modèles sont résolus dans un ordre fixe, et cet ordre compte :

  1. une surcharge d’environnement par adaptateur,
  2. l’environnement de l’appelant,
  3. le magasin d’identifiants du serveur (avec rafraîchissement d’un jeton OAuth s’il approche de l’expiration),
  4. l’environnement du processus.

Comme la surcharge par adaptateur arrive en premier, elle l’emporte silencieusement sur un compte de fournisseur connecté dans l’application. Si une exécution est facturée sur le mauvais compte, c’est là qu’il faut regarder en premier. Les identifiants du fournisseur sont globaux à l’hôte — partagés par tous les espaces de travail de l’installation.

Le jeton GitHub de l’exécution provient du courtier d’identifiants : un jeton en lecture seule, limité au dépôt de l’arbre de travail. Un push passe par la passerelle d’exécution d’agent, qui interroge la règle Pousser vers un dépôt distant avant de le transmettre, et aucune clé de tickets n’est injectée. Voir Bac à sable et sécurité.

L’exécution est lancée avec son arbre de travail comme répertoire de travail et — pour Claude Code — une configuration MCP générée afin que l’agent puisse atteindre la surface d’outils de Control Center. ACP et le bash du harnais reçoivent un environnement assaini ; Claude Code via NoSandboxAdapter hérite de l’environnement de l’hôte.

L’exécution renvoie en flux une séquence d’événements typés, que le processeur de flux persiste sous forme de deltas de messages, replie en segments de transcription et transforme en événements de messagerie :

Événement Ce qu’il transporte
thinking Sortie de raisonnement, rendue en ligne
text Sortie visible
toolCall Une invocation d’outil, avec son identifiant et ses arguments structurés
toolResult Le résultat correspondant, signalé lorsqu’il a échoué ou qu’il s’agit d’un flux partiel
usage Le nombre de jetons et le coût sur une fenêtre de mesure
error Une erreur, avec le code structuré de l’adaptateur lorsqu’il en a signalé un
sandboxViolation Le système d’exploitation a refusé un appel système. Émis uniquement là où un bac à sable encapsule réellement le processus — voir la note ci-dessus
debug Diagnostics d’exécution (marqueurs de lancement, décisions des garde-fous, rappels de contrat)
done L’exécution est terminée

Il n’y a délibérément aucun plafond de tours, nulle part. Un nombre d’itérations fixe tue du travail long légitime (migrations, nettoyage en éventail) et n’attrape rien que les garde-fous ci-dessous n’attraperaient plus tôt. Une exécution se termine lorsque :

  • le modèle s’arrête de lui-même ;
  • un humain l’arrête, ou prend le contrôle de la conversation (ce qui met le harnais intégré en pause à sa prochaine frontière de tour propre — une exécution Claude Code, qui n’a pas de frontière sûre, est arrêtée à la place) ;
  • le chien de garde d’inactivité se déclenche. Aucune sortie pendant le seuil et la session est interrompue avec un échec silentRun. La valeur par défaut est de 15 minutes, ramenée à 10 dans les modes essentiellement consultatifs (revue et plan) ; un agent peut la remplacer entre 1 et 240 minutes. Le chien de garde est armé lorsqu’un processus enfant signale son PID, il couvre donc la CLI Claude Code — pas le harnais intégré, qui ne lance aucun processus ;
  • un plafond de coût se déclenche — mais uniquement pour les commandes autonomes.

Le garde-fou contre les boucles infernales n’est pas un terminateur : trois appels d’outil identiques d’affilée et la boucle oriente l’agent vers un autre angle d’attaque.

Pris dans leur ensemble, un tour interactif ordinaire sur le harnais intégré n’a ni plafond de tours, ni plafond de coût, ni chien de garde d’inactivité. Il se termine lorsque le modèle s’arrête ou lorsque vous l’arrêtez. C’est un compromis délibéré — les alternatives tuent toutes du travail long légitime — mais cela signifie qu’une exécution interactive non surveillée n’est bornée par rien d’autre que le jugement du modèle lui-même.

En cours d’exécution, un message envoyé à un agent actif est mis en file comme pilotage et vidé à la frontière de tour suivante, ce qui vous permet d’orienter une exécution sans la tuer. Le pilotage, la mise en pause, les sous-agents et le contrat d’achèvement sont des fonctionnalités du harnais intégré ; Claude Code n’en a aucune.

Chaque lancement écrit un AgentRunLog : heures de début et de fin, résumé, nombre de jetons et coût estimé (y compris le coût agrégé depuis les sous-agents), adaptateur et modèle, identifiant de processus et chemin de journal, la conversation et tout ticket ou toute exécution de pipeline auquel il appartient, plus deux champs de classification qui portent l’essentiel de la vérité.

  • Le statut est grossier : pending, running, completed, error.
  • La vivacité est la lecture plus fine : alive, productive, completed, blocked, empty, looping, failed, stalled, dead.
  • La famille d’erreurs regroupe les échecs utilement : transientUpstream, sandboxInfrastructure, budgetExceeded, processLost, silentRun, unknown.

La clôture de l’exécution publie AgentRunCompleted, et beaucoup de choses en dépendent : l’agrégation des coûts de pipeline, la reprise d’une étape de pipeline qui attendait l’exécution, la clôture du ticket sous-jacent, la correspondance d’une orchestration avec un état terminal, la décision de poursuivre ou d’arrêter du superviseur d’objectif, le rôle de vérificateur et l’évaluation budgétaire après exécution. La liste de présence suit séparément, en observant les journaux d’exécution récents plutôt que l’événement. La notification « Agent terminé » ne figure pas dans cette liste : l’événement se déclenche à la fin du flux du processus, avant l’écriture de la réponse. La notification est donc le MessageReceived du tour, publié une fois le message enregistré.

L’application du budget est une réaction, pas un verrou. Lorsqu’une exécution se termine, l’évaluateur compare les dépenses de l’agent depuis le début du mois à son plafond effectif (une politique budgétaire liée s’il en existe une, sinon le montant mensuel propre à l’agent ; zéro signifie illimité). Franchir le seuil souple enregistre un incident d’avertissement ; épuiser le budget enregistre un incident dur et bascule le statut de cycle de vie de l’agent sur paused dans l’organigramme. Le lancement accepte toujours du travail de cet agent. Un déclencheur de pipeline BudgetThresholdCrossed peut lancer un travail de suivi à partir du même événement.

Chaque journal d’exécution peut porter une lignée de tentative — un identifiant d’exécution parente, un compteur de tentatives et un résumé de continuation. L’arbre d’exécution de la conversation affiche le numéro de tentative lorsqu’il est différent de zéro. Un « réessayer » depuis le chat est un nouveau lancement avec un journal d’exécution inédit.