La communication avec les Claude Managed Agents est basée sur des événements. Vous envoyez des événements utilisateur à l'agent et recevez en retour des événements d'agent et de session pour suivre l'état.
Toutes les requêtes à l'API Managed Agents nécessitent l'en-tête bêta managed-agents-2026-04-01. Le SDK définit automatiquement l'en-tête bêta.
Les événements circulent dans deux directions.
user.* lancent une session et la pilotent au fur et à mesure de sa progression ; system.message met à jour l'invite système de l'agent entre les tours.Les chaînes de type d'événement de session, de span, d'agent, d'utilisateur et de système suivent une convention de nommage {domain}.{action}. Les événements d'aperçu delta réservés au streaming (event_start, event_delta) font exception. Consultez Types d'événements dans la référence pour le catalogue complet.
Chaque événement persisté inclut un horodatage processed_at indiquant quand l'événement a été enregistré côté serveur. Si processed_at est null, cela signifie que l'événement a été mis en file d'attente par le harnais et qu'il est traité une fois que les événements précédents ont fini d'être traités.
Par défaut, le texte de réponse de l'agent arrive dans le flux sous forme d'événements agent.message mis en tampon, chacun émis uniquement une fois que la requête au modèle qui l'a produit est terminée. Les deltas d'événements vous permettent d'afficher ce texte de manière incrémentale, comme un aperçu en direct, pendant que le modèle est encore en train de le générer. Un aperçu n'est pas la réponse : les aperçus sont une aide à l'affichage fournie au mieux, et l'événement agent.message mis en tampon reste toujours l'enregistrement faisant autorité. Un client qui ignore les aperçus reçoit tout de même un flux complet et correct.
Les aperçus sont activés sur demande, par connexion de streaming. Ajoutez le paramètre de requête event_deltas[] à GET /v1/sessions/{session_id}/events/stream, en le répétant une fois pour chaque type d'événement que vous souhaitez prévisualiser. Les valeurs acceptées sont agent.message et agent.thinking ; toute autre valeur renvoie une erreur 400. Seul le flux d'événements au niveau de la session prend en charge ce paramètre. Les flux d'événements de threads de session le rejettent.
Lorsqu'un événement prévisualisé commence, le flux émet un event_start contenant le type et l'id de l'événement à venir :
{
"type": "event_start",
"event": {
"type": "agent.message",
"id": "sevt_01abc..."
}
}Pour agent.message, le début est suivi d'événements event_delta contenant du texte incrémental. Chaque delta nomme l'événement qu'il étend dans event_id et le bloc de contenu qu'il étend dans delta.index :
{
"type": "event_delta",
"event_id": "sevt_01abc...",
"delta": {
"type": "content_delta",
"index": 0,
"content": {
"type": "text",
"text": "Here is the summary"
}
}
}Lorsqu'un événement agent.thinking est prévisualisé, seul l'event_start est émis. Aucun événement event_delta ne suit, et le contenu arrive dans l'événement agent.thinking mis en tampon comme d'habitude.
Contrairement aux événements persistés, event_start et event_delta n'ont pas d'id ni de processed_at propres. Le seul identifiant qu'ils portent est l'id de l'événement qu'ils prévisualisent.
Les deltas d'événements utilisent un format de transmission différent de celui des messages en streaming, et cette différence est intentionnelle. Un agent.message prévisualisé reçoit un seul event_start suivi uniquement d'événements event_delta. Il n'y a pas d'événements de début ou de fin par bloc de contenu, ni d'événement de fin pour l'événement prévisualisé lui-même. Le type de delta est content_delta, et non content_block_delta. Le code d'accumulateur écrit pour l'API Messages ne peut pas être réutilisé tel quel.
Les SDK Python, TypeScript et Go incluent un utilitaire d'accumulateur qui indexe l'aperçu par l'id de l'événement et gère la comptabilité de l'index pour vous. Le modèle manuel fonctionne dans tous les langages : dans les autres SDK, appliquez-le aux types d'événements générés.
Dans le modèle manuel, traitez l'aperçu comme un tampon de travail et l'événement mis en tampon comme l'enregistrement de référence. Indexez le tampon par (event_id, index). Réconciliez par requête au modèle : un tour s'ouvre avec un seul événement session.status_running, puis sur un tour qui se termine normalement, chaque requête au modèle produit, dans l'ordre, span.model_request_start, event_start, les événements event_delta, l'agent.message mis en tampon, et enfin span.model_request_end (dans l'onglet Événements de span). Traitez chaque événement au fur et à mesure de son arrivée :
event_start, notez l'id annoncé. Les identifiants correspondent toujours : event_start.event.id, chaque event_delta.event_id, et l'id de l'agent.message mis en tampon sont la même valeur.event_delta, ajoutez delta.content.text à l'entrée située à (event_id, delta.index) et affichez le texte en cours. Le premier delta pour un index crée cette entrée.agent.message mis en tampon arrive, faites-le correspondre par id, supprimez l'aperçu accumulé et affichez le contenu du message à la place.span.model_request_end, fermez tout aperçu qui n'a pas été réconcilié par son événement mis en tampon. Aucun autre delta n'arrivera pour celui-ci. Si le tour génère une erreur ou est interrompu, l'événement mis en tampon peut ne jamais arriver ; span.model_request_end arrive quand même.# Instantanés d'aperçu, indexés par id d'événement. accumulate_managed_agents_event agrège chaque
# event_start / event_delta en un instantané agent.message ; le
# agent.message mis en tampon le remplace.
previews: dict[str, BetaManagedAgentsAgentMessageEvent] = {}
# Activez les aperçus agent.message sur cette connexion
with client.beta.sessions.events.stream(
session.id, event_deltas=["agent.message"]
) as stream:
client.beta.sessions.events.send(
session.id,
events=[
{
"type": "user.message",
"content": [{"type": "text", "text": "Describe the repo in one sentence."}],
},
],
)
for event in stream:
match event.type:
case "event_start":
snapshot = accumulate_managed_agents_event(None, event)
if snapshot is not None:
previews[event.event.id] = snapshot
print(f"event_start {event.event.type} {event.event.id}")
case "event_delta":
preview = accumulate_managed_agents_event(previews.get(event.event_id), event)
if preview is not None:
previews[event.event_id] = preview
text = "".join(block.text for block in preview.content)
print(f"event_delta preview: {text!r}")
case "agent.message":
# L'événement mis en tampon fait foi : il remplace et ferme l'aperçu
preview = accumulate_managed_agents_event(previews.pop(event.id, None), event)
text = "".join(block.text for block in preview.content)
print(f"agent.message {event.id} {text!r}")
case "span.model_request_end":
# Plus aucun delta n'arrive. Fermez tout aperçu dont
# l'événement mis en tampon n'est jamais arrivé.
for event_id in previews:
print(f"span.model_request_end closing preview for {event_id}")
previews.clear()
case "session.status_idle":
breakLes aperçus sont optimisés pour la réactivité. Développez en tenant compte de ces contraintes :
agent.message mis en tampon arrive tout de même complet. Ne traitez jamais un aperçu accumulé comme définitif.agent.message que votre aperçu attendait. Il n'existe aucun moyen de redemander les deltas manqués.agent.thinking avec début uniquement : Un aperçu agent.thinking n'émet que l'event_start comme signal qu'un bloc de réflexion a commencé ; aucun événement event_delta ne le suit.event_start et event_delta n'existent que sur le flux en direct. Ils n'apparaissent pas dans l'historique des événements de la session (GET /v1/sessions/{session_id}/events).Lorsque l'agent invoque un outil personnalisé :
agent.custom_tool_use contenant le nom de l'outil et l'entrée.session.status_idle contenant stop_reason: requires_action. Les identifiants des événements bloquants se trouvent dans le tableau stop_reason.event_ids.user.custom_tool_result pour chacun, en passant l'identifiant de l'événement dans le paramètre custom_tool_use_id avec le contenu du résultat.running.with client.beta.sessions.events.stream(session.id) as stream:
for event in stream:
if event.type == "session.status_idle" and (stop_reason := event.stop_reason):
match stop_reason.type:
case "requires_action":
for event_id in stop_reason.event_ids:
# Rechercher l'événement d'utilisation d'outil personnalisé et l'exécuter
tool_event = events_by_id[event_id]
result = call_tool(tool_event.name, tool_event.input)
# Renvoyer le résultat
client.beta.sessions.events.send(
session.id,
events=[
{
"type": "user.custom_tool_result",
"custom_tool_use_id": event_id,
"content": [{"type": "text", "text": result}],
},
],
)
case "end_turn":
breakLorsqu'une politique de permissions exige une confirmation avant l'exécution d'un outil :
agent.tool_use ou agent.mcp_tool_use.session.status_idle contenant stop_reason: requires_action. Les identifiants des événements bloquants se trouvent dans le tableau stop_reason.event_ids.user.tool_confirmation pour chacun, en passant l'identifiant de l'événement dans le paramètre tool_use_id. Définissez result sur "allow" ou "deny". Utilisez deny_message pour expliquer un refus.running.with client.beta.sessions.events.stream(session.id) as stream:
for event in stream:
if event.type == "session.status_idle" and (stop_reason := event.stop_reason):
match stop_reason.type:
case "requires_action":
for event_id in stop_reason.event_ids:
# Approuver l'appel d'outil en attente
client.beta.sessions.events.send(
session.id,
events=[
{
"type": "user.tool_confirmation",
"tool_use_id": event_id,
"result": "allow",
},
],
)
case "end_turn":
breakLes sessions persistent entre les interactions. L'historique de conversation est préservé à moins que la session ne soit explicitement supprimée. Lorsqu'une session devient inactive, son sandbox fait l'objet d'un point de contrôle, préservant l'état complet du sandbox, y compris le système de fichiers, les paquets installés et tous les fichiers créés par l'agent. Cela vous permet de reprendre proprement après une période d'inactivité.
Bien que l'historique de session soit persisté jusqu'à sa suppression, les points de contrôle ne sont conservés que pendant 30 jours après la dernière activité de la session. Si votre flux de travail nécessite que l'état complet du sandbox (fichiers, outils installés, etc.) persiste au-delà de 30 jours, envoyez périodiquement des événements user.message pour réinitialiser le minuteur d'inactivité avant l'expiration du point de contrôle.
Pour reprendre une session, envoyez-lui un événement user.message comme d'habitude :
# En production, passez l'ID stocké de la session que vous souhaitez reprendre.
ant beta:sessions:events send --session-id "$SESSION_ID" <<'YAML'
events:
- type: user.message
content:
- type: text
text: Now run the tests against the changes you made earlier.
YAMLsystem.message n'est actuellement pris en charge que par Claude Opus 4.8. Si un modèle configuré sur l'agent ne prend pas en charge l'injection système en cours de conversation, l'événement est rejeté avec une erreur de validation model_does_not_support_mid_conversation_system.
Envoyez un événement system.message pour mettre à jour l'invite système de l'agent entre les tours. Contrairement au champ system de la définition de l'agent (qui est fixé à la création de la session), system.message vous permet de modifier l'invite système au fur et à mesure de la progression de la session. Utilisez-le lorsque l'agent a besoin de directives système mises à jour en cours de session : une persona différente, des contraintes révisées, ou du contexte récupéré à l'exécution qui doit façonner le comportement du modèle pour la suite.
ant beta:sessions:events send --session-id "$SESSION_ID" <<'YAML'
events:
- type: system.message
content:
- type: text
text: "The user's current timezone is America/New_York."
YAMLsystem.message ne peut pas être envoyé lorsque la session est inactive avec stop_reason: requires_action. content accepte de 1 à 1000 éléments de texte.
L'objet session inclut un champ usage avec des statistiques cumulatives de tokens. Récupérez la session après qu'elle soit devenue inactive pour lire les derniers totaux, et utilisez-les pour suivre les coûts, appliquer des budgets ou surveiller la consommation.
{
"id": "sesn_01...",
"status": "idle",
"usage": {
"input_tokens": 5000,
"output_tokens": 3200,
"cache_creation_input_tokens": 2000,
"cache_read_input_tokens": 20000
}
}input_tokens indique les tokens d'entrée non mis en cache et output_tokens indique le total des tokens de sortie sur l'ensemble des appels au modèle dans la session. Les champs cache_creation_input_tokens et cache_read_input_tokens reflètent l'activité de mise en cache des prompts. Les entrées de cache utilisent un TTL de 5 minutes, de sorte que les tours consécutifs dans cette fenêtre bénéficient de lectures de cache, ce qui réduit le coût par token.
La Console fournit une vue chronologique visuelle de vos sessions d'agent. Accédez à la section Claude Managed Agents dans la Console pour voir :
session.errorWas this page helpful?