---
title: Exécutions de workflow
url: https://platform.claude.com/docs/fr/managed-agents/workflow-runs
description: "Suivez les exécutions de workflow d'un agent : leurs états et événements, le moment où le travail est terminé, les requêtes pendant qu'une exécution est ouverte, les budgets et les limites."
featureMetadata:
  topic:
    title: Managed Agents
    url: https://platform.claude.com/docs/en/managed-agents/overview
  status: beta
  betaHeader: managed-agents-2026-04-01
---

Un **« workflow » (flux de travail)** est un programme qu'un agent écrit pour exécuter de nombreux agents et combiner ce qu'ils renvoient. Une **« workflow run » (exécution de workflow)** est l'exécution d'un workflow. Les **[workflows dynamiques](https://platform.claude.com/docs/fr/managed-agents/multiagent-orchestration#dynamic-workflows)** sont la fonctionnalité qui permet à un agent d'écrire des workflows et de démarrer des exécutions. Vous l'activez ou la désactivez avec le paramètre `workflows` (csharp, go: `Workflows`) dans le bloc `multiagent` (csharp, go: `Multiagent`) de l'agent.

Le serveur exécute un workflow en arrière-plan. Ses agents travaillent dans des [fils de session](https://platform.claude.com/docs/fr/managed-agents/session-threads) que le serveur crée au fur et à mesure que le workflow en a besoin. Vous suivez les exécutions sur le [flux d'événements](https://platform.claude.com/docs/fr/managed-agents/events-and-streaming) de la session. Pour arrêter une exécution, demandez à l'agent de l'arrêter (voir [Interrompre une session avec des exécutions ouvertes](https://platform.claude.com/docs/fr/managed-agents/workflow-runs#interrupt-a-session-with-runs-open)).

## Fonctionnement des workflows dynamiques

L'agent que la session exécute écrit chaque workflow pour le travail que vous décrivez. Un workflow est un programme : il exécute d'autres agents, collecte ce que chacun renvoie et combine les résultats. Ainsi, l'agent peut prendre en charge une tâche trop volumineuse pour une seule conversation, comme l'examen de centaines de documents. Pendant une exécution, l'agent peut continuer à travailler ou terminer son tour, et il peut vérifier l'état de l'exécution.

<Frame>
  ![Couches d'une « workflow run » (exécution de workflow) : l'exécution, ses deux phases et les « agent threads » (fils d'agent) de chacune. Les résultats passent d'une phase à la suivante.](https://platform.claude.com/docs/fr/images/workflow-anatomy.svg)
</Frame>

Le diagramme montre un exemple. Chaque workflow que l'agent écrit possède ses propres phases et agents. Une exécution comporte ces couches :

* **Exécution de workflow :** Le serveur exécute le workflow en arrière-plan, sous la forme d'une exécution de workflow. Une session peut avoir plusieurs exécutions ouvertes en même temps.
* **Phases :** Un workflow peut diviser son travail en phases. Une phase est une étape nommée de l'exécution, comme « Read the contracts ». Vous suivez la progression d'une exécution grâce à ses événements de phase.
* **Fils d'agent :** Dans une phase, le programme exécute des agents. Chaque agent travaille dans son propre [fil de session](https://platform.claude.com/docs/fr/managed-agents/session-threads), sur un prompt que le programme a écrit. Un agent d'une exécution peut être un agent inline, que le programme définit lui-même, ou un agent prédéfini, que vous indiquez dans la [liste `predefined_agents` (java, php: `predefinedAgents`; csharp, go: `PredefinedAgents`) de `workflows` (csharp, go: `Workflows`)](https://platform.claude.com/docs/fr/managed-agents/multiagent-orchestration#predefined-and-inline-agents). Pour savoir ce que montre chaque fil, consultez [Les fils d'une exécution](https://platform.claude.com/docs/fr/managed-agents/workflow-runs#a-runs-threads).

Le programme peut faire ce qui suit :

* **Exécuter des agents en même temps :** Le programme peut exécuter de nombreux agents en même temps, ce qu'on appelle le « fanning out » (déploiement en éventail). Dans le diagramme, trois agents lisent des contrats dans la première phase.
* **Transmettre les résultats d'un agent à un autre :** Chaque agent renvoie son résultat au programme. Le programme peut transmettre ce résultat à un autre agent. Dans le diagramme, l'agent de la deuxième phase travaille avec ce que les trois premiers ont renvoyé. Les agents d'une exécution travaillent aussi avec les mêmes fichiers, dans la « sandbox » (bac à sable) de la session.
* **Passer à l'étape suivante de lui-même :** Le résultat d'un agent va au programme, et non à l'agent que la session exécute. Le programme détermine quels agents s'exécutent ensuite, et il écrit leurs prompts.
* **Répéter et choisir :** À l'intérieur d'une phase, le programme peut répéter un travail et choisir son étape suivante en fonction de ce qu'un agent a renvoyé. Par exemple, il peut faire réviser un brouillon jusqu'à ce qu'une relecture soit validée ou qu'un nombre défini de tours soit épuisé. Dans le diagramme, le programme peut répéter une étape à l'intérieur de la deuxième phase.
* **Gérer un agent en échec :** Lorsque l'un de ses agents échoue, le serveur exécute généralement cet agent une fois de plus, sur un nouveau fil, pendant que les autres agents de l'exécution continuent de travailler. S'il échoue à nouveau, le programme peut gérer l'échec ou le laisser mettre fin à l'exécution. Si le programme gère un échec, l'exécution peut tout de même se terminer avec `completed` ; demandez donc à l'agent que la session exécute de signaler ce qui a échoué.

Lorsque l'exécution se termine, l'agent que la session exécute obtient un tour pour lire ce que l'exécution a fait. Il peut alors vous répondre ou démarrer une autre exécution. [Événements d'exécution](https://platform.claude.com/docs/fr/managed-agents/workflow-runs#run-events) liste les cas où ce tour arrive plus tard ou n'arrive pas.

Vous pouvez orienter la manière dont une exécution effectue le travail, par exemple la façon dont elle répartit le travail et ce qu'elle fait lorsqu'un agent échoue. Consultez [Indiquer à l'agent quand utiliser une exécution](https://platform.claude.com/docs/fr/managed-agents/multiagent-orchestration#tell-the-agent-when-to-use-a-run).

Il n'y a pas d'étape d'approbation avant le démarrage d'une exécution : l'agent démarre une exécution dans le même tour que celui où il écrit le workflow. Pour examiner d'abord un plan, demandez à l'agent de décrire l'exécution et d'attendre votre feu vert. Au démarrage d'une exécution, `workflow_run.created` indique son nom, sa description et ses phases. Si une exécution que vous n'avez pas approuvée démarre, demandez à l'agent de l'arrêter (voir [Interrompre une session avec des exécutions ouvertes](https://platform.claude.com/docs/fr/managed-agents/workflow-runs#interrupt-a-session-with-runs-open)).

## Comment une exécution passe d'un état à l'autre

<Frame>
  ![États d'une « workflow run » (exécution de workflow) : « running » (en cours), « idle » (inactive) et « ended » (terminée), et ce qui fait passer une exécution de l'un à l'autre.](https://platform.claude.com/docs/fr/images/workflow-run-states.svg)
</Frame>

Une exécution démarre en cours (« running ») ou inactive (« idle »). Atteindre le budget, par exemple, met en pause une exécution en cours, ce qui la rend inactive ; augmenter ou supprimer le budget la fait alors reprendre, sauf si une interruption l'a aussi mise en pause. Une exécution en cours se termine lorsque son workflow se termine, que l'agent l'arrête, qu'elle échoue, que sa durée de vie s'écoule ou que la session est archivée. Une exécution inactive peut aussi se terminer, par exemple lorsque l'agent l'arrête ou que la session est archivée.

Une exécution est **ouverte** depuis son événement `workflow_run.created` jusqu'à son événement `workflow_run.status_ended`, qu'elle soit en cours ou inactive. Une exécution est inactive tant qu'elle est en pause, par exemple au budget de la session. Une exécution a une [durée de vie](https://platform.claude.com/docs/fr/managed-agents/workflow-runs#limits), que l'agent peut définir lorsqu'il démarre l'exécution. Le temps qu'une exécution passe à attendre votre client compte dans cette durée de vie. Une pause n'empêche pas la durée de vie d'une exécution de s'écouler ; une exécution qui reste en pause peut donc se terminer avec `timeout_error`. Les événements suivants signalent le démarrage d'une exécution, ses phases et sa fin. Une pause au budget en envoie également un. Une pause après une interruption peut n'en envoyer aucun. Chaque événement `workflow_run.*` inclut `workflow_run_id` (java: `workflowRunId`; php: `workflowRunID`; csharp, go: `WorkflowRunID`), qui vaut `null` (python: `None`; ruby: `nil`; java: `Optional.empty()`; go: `""`) uniquement sur un `workflow_run.error` lorsqu'aucune exécution n'a été créée.

## Événements d'exécution

Les événements d'exécution arrivent sur le flux d'événements de la session, qui est le flux du fil principal, et la liste des événements de la session les renvoie également. Les événements d'exécution ne déclenchent pas de [webhooks](https://platform.claude.com/docs/fr/managed-agents/webhooks). Les événements de statut des fils de l'exécution arrivent sur le même flux. Chacun nomme son fil dans `session_thread_id` (java: `sessionThreadId`; php: `sessionThreadID`; csharp, go: `SessionThreadID`), et les fils d'une exécution sont ceux dont l'événement `session.thread_created` avait le `workflow_run_id` (java: `workflowRunId`; php: `workflowRunID`; csharp, go: `WorkflowRunID`) de l'exécution.

| Événement                                                | Quand il arrive                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           | Que faire                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| -------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `workflow_run.created`                                   | L'agent a démarré une exécution. Inclut `workflow_run_id` (java: `workflowRunId`; php: `workflowRunID`; csharp, go: `WorkflowRunID`) (`wrun_…`), le `name` (csharp, go: `Name`) et la `description` (csharp, go: `Description`) de l'exécution, ainsi que `phases` (csharp, go: `Phases`), les phases que le workflow déclare, chacune avec un `id` (csharp, go: `ID`), un `name` (csharp, go: `Name`) et une `description` (csharp, go: `Description`). Une `description` (csharp, go: `Description`) vaut `null` (python: `None`; ruby: `nil`; java: `Optional.empty()`; go: `""`) lorsque le workflow n'en fournit aucune. `phases` (csharp, go: `Phases`) est toujours présent et peut être vide. Le `name` (csharp, go: `Name`) et la `description` (csharp, go: `Description`) de l'exécution et des phases sont du texte que le modèle a écrit ; ils peuvent donc reprendre des mots de votre requête. Le `name` (csharp, go: `Name`) d'une exécution peut également être attribué par le serveur. | Enregistrez l'exécution comme ouverte. Affichez son `name` (csharp, go: `Name`), ainsi que la progression par rapport à `phases` (csharp, go: `Phases`).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `workflow_run.status_running`                            | Lorsque l'exécution commence à s'exécuter, ce qui peut survenir un certain temps après `created`, et chaque fois qu'elle reprend après une pause au budget. Une reprise après une interruption peut ne pas l'envoyer. Une exécution qui démarre inactive peut recevoir `workflow_run.status_idle` en premier.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             | Affichez l'exécution comme en cours.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `workflow_run.status_idle`                               | L'exécution a été mise en pause, par exemple au budget de la session. L'événement n'indique pas pourquoi. Une pause après une interruption peut ne pas l'envoyer.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         | Pour continuer, consultez [Coût et budgets](https://platform.claude.com/docs/fr/managed-agents/workflow-runs#cost-and-budgets) ou [Interrompre une session avec des exécutions ouvertes](https://platform.claude.com/docs/fr/managed-agents/workflow-runs#interrupt-a-session-with-runs-open).                                                                                                                                                                                                                                                                                                                                                   |
| `workflow_run.phase_started`, `workflow_run.phase_ended` | Le workflow est entré dans une phase ou en est sorti, ou la fin de l'exécution a clôturé une phase encore ouverte. L'événement de fin n'indique pas si le travail de la phase s'est terminé. Les deux incluent `workflow_run_phase_id` (java: `workflowRunPhaseId`; php: `workflowRunPhaseID`; csharp, go: `WorkflowRunPhaseID`). L'événement de fin contient également `phase_started_id` (java: `phaseStartedId`; php: `phaseStartedID`; csharp, go: `PhaseStartedID`), l'`id` (csharp, go: `ID`) de l'événement de début qu'il clôture. Aucun des deux ne contient le nom de la phase : recherchez-le par `workflow_run_phase_id` (java: `workflowRunPhaseId`; php: `workflowRunPhaseID`; csharp, go: `WorkflowRunPhaseID`) dans les `phases` (csharp, go: `Phases`) de `workflow_run.created`.                                                                                                                                                                                                        | Mettez à jour la progression. Les phases s'exécutent une à la fois, dans l'ordre de `phases` (csharp, go: `Phases`), chacune au plus une fois, mais l'API ne le garantit pas. Associez la fin d'une phase à son début grâce à `phase_started_id` (java: `phaseStartedId`; php: `phaseStartedID`; csharp, go: `PhaseStartedID`). Gérez le cas de plusieurs phases ouvertes, d'une phase qui ne figure pas dans `phases` (csharp, go: `Phases`) et d'une phase listée qui ne démarre jamais, même dans une exécution qui va jusqu'à son terme. Chaque phase qui démarre se termine également, avant le `workflow_run.status_ended` de l'exécution. |
| `workflow_run.status_ended`                              | L'exécution s'est terminée. Toujours le dernier des événements `workflow_run.*` de l'exécution. Inclut `result` (csharp, go: `Result`).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   | Lisez `result` (csharp, go: `Result`) (tableau suivant). L'agent obtient ensuite un tour pour lire comment l'exécution s'est terminée. Au budget, ou pendant que le fil principal attend votre client, ce tour arrive plus tard. Après une interruption, ce tour peut ne pas arriver : envoyez un `user.message`, ou lisez `result` (csharp, go: `Result`) vous-même. Après un archivage ou une terminaison, il n'arrive pas.                                                                                                                                                                                                                    |
| `workflow_run.error`                                     | Le serveur signale une erreur d'une exécution, ou un démarrage qu'il a refusé. Une exécution qui se termine en `error` reçoit cet événement, avec la même erreur, avant son `workflow_run.status_ended`. Inclut `error` (csharp, go: `Error`) : un `type` (csharp, go: `Type`) et un `message` (csharp, go: `Message`) qui peut être journalisé sans risque. `workflow_run_id` (java: `workflowRunId`; php: `workflowRunID`; csharp, go: `WorkflowRunID`) vaut `null` (python: `None`; ruby: `nil`; java: `Optional.empty()`; go: `""`) lorsqu'aucune exécution n'a été créée.                                                                                                                                                                                                                                                                                                                                                                                                                            | Journalisez-le, et ne le considérez pas comme la fin de l'exécution. Si `workflow_run_id` (java: `workflowRunId`; php: `workflowRunID`; csharp, go: `WorkflowRunID`) vaut `null` (python: `None`; ruby: `nil`; java: `Optional.empty()`; go: `""`), aucune exécution n'a démarré. Sinon, continuez à suivre l'exécution jusqu'à son `workflow_run.status_ended`.                                                                                                                                                                                                                                                                                 |

| `result`                          | Signification                                                                                                                                                                                                                                                                                                                                                                               |
| --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `{"type": "completed"}`           | Le workflow a fini de s'exécuter. Le résultat n'indique pas si le travail a réussi. Une exécution peut se terminer en `completed` même si du travail sur ses fils a échoué, ou si un fil n'a pas pu être créé. Pour trouver le travail en échec, lisez les événements de chacun des [fils de l'exécution](https://platform.claude.com/docs/fr/managed-agents/workflow-runs#a-runs-threads). |
| `{"type": "stopped"}`             | L'agent a arrêté l'exécution, ou la session a été archivée. L'événement n'indique pas lequel des deux. Ne considérez pas qu'il s'agit des seules causes possibles.                                                                                                                                                                                                                          |
| `error` avec `timeout_error`      | L'exécution a atteint sa [durée de vie](https://platform.claude.com/docs/fr/managed-agents/workflow-runs#limits), que l'agent peut définir.                                                                                                                                                                                                                                                 |
| `error` avec `program_error`      | Le workflow a échoué. Son code a échoué, ou il a enfreint une règle des workflows, autre qu'une limite. Ou l'un des fils de l'exécution a échoué, ou n'a pas pu être créé, et le workflow a laissé cela mettre fin à l'exécution.                                                                                                                                                           |
| `error` avec `thread_limit_error` | L'exécution a dépassé sa [limite sur les agents qu'un workflow démarre](https://platform.claude.com/docs/fr/managed-agents/workflow-runs#limits).                                                                                                                                                                                                                                           |
| `error` avec `unknown_error`      | Le serveur n'a pas pu poursuivre l'exécution, ou l'exécution a dépassé l'une des autres limites du serveur sur les workflows.                                                                                                                                                                                                                                                               |

Un résultat d'erreur ressemble à `{"type": "error", "error": {"type": "timeout_error", "message": "..."}}`, où `message` peut être journalisé sans risque. Traitez un `result.type` non reconnu comme une exécution qui s'est terminée d'une autre manière, et un `error.type` non reconnu comme une erreur. Lorsqu'un élément dont dépend la session échoue, comme le modèle, un serveur MCP, des identifiants ou la facturation, le flux du fil en échec reçoit un `session.error`. Cela ne met pas fin à une exécution à lui seul. Mais si cela fait échouer l'un des fils de l'exécution, et que le workflow laisse cela mettre fin à l'exécution, l'exécution se termine avec `program_error`.

Par exemple, vous demandez à l'[agent de révision de contrats](https://platform.claude.com/docs/fr/managed-agents/multiagent-orchestration#turn-on-dynamic-workflows) lesquels de 300 contrats comportent une clause de changement de contrôle, et l'agent démarre une exécution :

1. `workflow_run.created` nomme l'exécution « Find change-of-control clauses » et liste les phases « Read the contracts » et « Reconcile the findings » dans `phases` (csharp, go: `Phases`). Puis `workflow_run.status_running` suit.
2. Des événements de phase marquent chaque phase, et chaque fil que l'exécution crée envoie `session.thread_created` avec le `workflow_run_id` (java: `workflowRunId`; php: `workflowRunID`; csharp, go: `WorkflowRunID`) de l'exécution.
3. `workflow_run.status_ended` arrive avec `result: {"type": "completed"}`.
4. L'agent répond : « 41 des 300 contrats en comportent une », et `session.status_idle` arrive avec `end_turn`.

Le premier événement de l'exécution liste ses phases :

```json
{
  "type": "workflow_run.created",
  "id": "sevt_01abc...",
  "workflow_run_id": "wrun_01J8XkN5uT3vHpLqRfWdY2",
  "name": "Find change-of-control clauses",
  "description": "Reads each contract and lists those that have the clause.",
  "phases": [
    {
      "id": "wrph_01Kd3a1f3",
      "name": "Read the contracts",
      "description": "Reads each contract for the clause."
    },
    { "id": "wrph_01Kd3b7c9", "name": "Reconcile the findings", "description": null }
  ],
  "processed_at": "2026-10-09T14:01:45Z"
}
```

Chaque événement de phase nomme sa phase par `workflow_run_phase_id`. Il s'agit d'un `id` de `phases`, mais l'API ne le garantit pas :

```json
{
  "type": "workflow_run.phase_started",
  "id": "sevt_01def...",
  "workflow_run_id": "wrun_01J8XkN5uT3vHpLqRfWdY2",
  "workflow_run_phase_id": "wrph_01Kd3a1f3",
  "processed_at": "2026-10-09T14:01:46Z"
}
```

Le dernier événement de l'exécution indique comment elle s'est terminée :

```json
{
  "type": "workflow_run.status_ended",
  "id": "sevt_01ghi...",
  "workflow_run_id": "wrun_01J8XkN5uT3vHpLqRfWdY2",
  "result": { "type": "completed" },
  "processed_at": "2026-10-09T14:09:12Z"
}
```

## Les fils d'une exécution

Chaque agent d'une exécution travaille dans son propre [fil de session](https://platform.claude.com/docs/fr/managed-agents/session-threads), que le serveur crée au fur et à mesure que le workflow en a besoin. Vous pouvez lister, lire et diffuser en streaming les fils d'une exécution comme n'importe quel fil enfant, et répondre à leurs appels d'outils depuis le flux principal. Pour les arrêter, demandez à l'agent d'arrêter l'exécution (voir [Interrompre une session avec des exécutions ouvertes](https://platform.claude.com/docs/fr/managed-agents/workflow-runs#interrupt-a-session-with-runs-open)).

* **Regroupement :** Un fil d'une exécution possède le `workflow_run_id` (java: `workflowRunId`; php: `workflowRunID`; csharp, go: `WorkflowRunID`) de l'exécution, tout comme l'événement `session.thread_created` qui l'annonce. Les autres fils, ainsi que les événements `session.thread_created` qui les annoncent, ont `workflow_run_id` (java: `workflowRunId`; php: `workflowRunID`; csharp, go: `WorkflowRunID`) défini sur `null` (python: `None`; ruby: `nil`; java: `Optional.empty()`; go: `""`).
* **Agent :** `agent` (csharp, go: `Agent`) indique l'agent que le fil exécute. Pour un agent que vous avez indiqué dans la [liste `predefined_agents` (java, php: `predefinedAgents`; csharp, go: `PredefinedAgents`) de `workflows` (csharp, go: `Workflows`)](https://platform.claude.com/docs/fr/managed-agents/multiagent-orchestration#turn-on-dynamic-workflows) dans `multiagent` (csharp, go: `Multiagent`), `agent` (csharp, go: `Agent`) contient l'`id` (csharp, go: `ID`) et la `version` (csharp, go: `Version`) de cet agent, comme sur le fil d'un sous-agent que vous avez indiqué. Pour un agent que le workflow définit (un agent inline), `agent` (csharp, go: `Agent`) a le `type` `inline` et aucun `id` (csharp, go: `ID`) ni `version` (csharp, go: `Version`). Il possède l'invite système que le workflow a écrite, et non celle de l'agent de la session. Il possède également le nom et la description que le workflow lui a donnés. Le serveur attribue un nom si le workflow n'en a donné aucun. Il utilise le modèle de l'agent de la session, l'agent que la session exécute. Ses outils, serveurs MCP et skills sont un sous-ensemble de ceux de l'agent de la session. Il les reçoit tous, mais l'API ne le garantit pas. Ses outils conservent leurs politiques d'autorisation.
* **Ce que les fils partagent :** Les fils d'une exécution travaillent dans la sandbox de la session ; chaque fil travaille donc avec les mêmes fichiers. Cela inclut les fichiers d'un magasin de mémoire que la session monte. Un agent que le workflow définit utilise ses serveurs MCP avec les identifiants que la session résout pour eux. Chaque fil possède son propre historique de conversation.
* **Événements :** Les événements `session.thread_created`, `session.thread_status_running`, `session.thread_status_idle` et `session.thread_status_terminated` d'un fil d'exécution arrivent aussi sur le flux principal (consultez [Événements d'exécution](https://platform.claude.com/docs/fr/managed-agents/workflow-runs#run-events)). Ses événements de message restent sur son propre flux. Ses webhooks de fil sont envoyés comme pour n'importe quel fil enfant. Pour savoir ce que le flux propre du fil enregistre, consultez [Événements des fils de session](https://platform.claude.com/docs/fr/managed-agents/session-threads#session-thread-events).
* **Phases :** Aucun événement ni champ n'indique dans quelle phase un fil travaille, et les fils d'une même exécution peuvent avoir le même `agent_name`. Suivez la progression d'une exécution grâce à ses événements de phase, et distinguez ses fils par `session_thread_id` (java: `sessionThreadId`; php: `sessionThreadID`; csharp, go: `SessionThreadID`).
* **Archivage :** Le serveur archive chaque fil au plus tard à la fin de son exécution. Il peut en archiver un plus tôt, dès que le fil renvoie son résultat ou que l'exécution n'en a plus besoin. Si le fil est encore en cours d'exécution ou attend votre client à ce moment-là, le serveur l'arrête d'abord. Un fil archivé reste dans la liste des fils, avec le statut `terminated`. Vous n'avez pas besoin d'archiver vous-même les fils d'une exécution.
* **Outils :** Une exécution peut créer plusieurs fils pour la même portion de travail ; faites donc en sorte que les outils que vos agents appellent puissent être appelés deux fois sans risque.

<Tip>
  Vous pouvez demander à l'agent le workflow qu'il a écrit pour votre requête. Attendez que l'exécution soit terminée et que la session soit `idle`. Envoyez ensuite un `user.message` qui demande à l'agent d'afficher mot pour mot le workflow avec lequel il a démarré l'exécution, et de ne pas démarrer une autre exécution.

  ```text wrap
  Print the workflow that you started the run with, word for word, in one code block. Do not start another run.
  ```
</Tip>

## Savoir quand le travail est terminé

Pendant qu'une exécution est en cours, attendez-vous à ce que la session reste `running`, même lorsqu'aucun de ses fils ne travaille. Elle passe à `idle` avec `requires_action` lorsqu'aucun fil ne travaille et qu'un fil attend votre client. Un état inactif à lui seul ne signifie pas que le travail est terminé. Le travail est terminé lorsque les deux conditions suivantes sont vraies :

1. Chaque exécution dont vous avez vu la création a son `workflow_run.status_ended`.
2. Ensuite, un `session.status_idle` arrive avec `stop_reason` (java, php: `stopReason`; csharp, go: `StopReason`) `end_turn`, et ce n'est pas votre propre requête, comme une interruption, qui l'a provoqué. Après une interruption, ne comptez qu'un état inactif qui survient après votre prochain `user.message` ou `user.define_outcome`.

* **Exécutions en pause :** Une exécution en pause ne maintient pas la session en `running`, de sorte que la session peut devenir inactive alors que l'exécution est encore ouverte. Au budget, par exemple, la session devient inactive avec `budget_reached`. Le travail n'est pas terminé tant que l'exécution ne s'est pas terminée.
* **Une autre exécution :** L'agent peut démarrer une nouvelle exécution lorsqu'il lit un résultat, vérifiez donc à nouveau.
* **Résultats attendus :** Si vous avez [défini un résultat attendu](https://platform.claude.com/docs/fr/managed-agents/define-outcomes), aucune évaluation ne démarre tant qu'une exécution est ouverte, qu'elle soit en cours ou inactive. Le tour au cours duquel l'agent lit le résultat de l'exécution peut en démarrer une.
* **`retries_exhausted` :** Le tour de l'agent a échoué sur une erreur : les nouvelles tentatives ont été épuisées, ou l'erreur ne peut pas faire l'objet d'une nouvelle tentative, comme un échec de facturation. Une exécution peut encore être en cours lorsque cet état inactif survient. Si une exécution s'est terminée et que l'agent n'a pas encore lu son résultat, le serveur démarre un nouveau tour sans aucune entrée de votre part. La session repasse à `running` ; attendez donc le prochain état inactif. Si la session reste inactive, lisez le `session.error` qui l'a précédé et corrigez la cause. Envoyez ensuite un `user.message`, ou lisez vous-même le `result` (csharp, go: `Result`) de chaque exécution.

## Suivre une exécution

Cet exemple suit une session depuis votre message jusqu'à la réponse de l'agent. Il ouvre le flux et envoie le message. Il fait ensuite ce qui suit :

* **Suit chaque exécution** depuis son `workflow_run.created` jusqu'à son `workflow_run.status_ended`, et affiche chaque phase lorsqu'elle démarre.
* **Répond aux appels d'outils personnalisés** à l'arrivée de chaque `agent.custom_tool_use`, car un fil d'une exécution peut attendre votre client pendant que la session reste `running`. Si les outils de votre agent [demandent une confirmation](https://platform.claude.com/docs/fr/managed-agents/events-and-streaming#tool-confirmation), ajoutez une branche qui répond à chaque `agent.tool_use` ou `agent.mcp_tool_use` dont le `evaluated_permission` (java, php: `evaluatedPermission`; csharp, go: `EvaluatedPermission`) vaut `ask`. L'exemple n'en contient pas, car une branche qui autorise chaque appel transformerait `always_ask` en autorisation systématique.
* **S'arrête** lorsque le [travail est terminé](https://platform.claude.com/docs/fr/managed-agents/workflow-runs#know-when-the-work-is-done) : aucune exécution n'est ouverte, et la session devient inactive avec `end_turn`. Il s'arrête aussi si la session se termine. Sur un état inactif avec toute autre raison d'arrêt, à l'exception de `requires_action`, comme `budget_reached`, `retries_exhausted` ou `refusal`, il affiche la raison et s'arrête ; gérez donc ces cas dans votre propre code. Il s'arrête sur `retries_exhausted` même lorsque le serveur est sur le point de démarrer un nouveau tour de lui-même. Il continue d'attendre sur `requires_action`, et sur `end_turn` tant qu'une exécution est ouverte.

<CodeGroup exclude="shell">
  <CodeGroupItem language="cURL">
    Ce flux de travail nécessite plus de logique qu'une seule commande shell. Utilisez le [SDK Claude](https://platform.claude.com/docs/fr/cli-sdks-libraries/overview) pour l'exécuter.
  </CodeGroupItem>

  <CodeGroupItem language="CLI">
    Ce flux de travail nécessite plus de logique qu'une seule commande shell. Utilisez le [SDK Claude](https://platform.claude.com/docs/fr/cli-sdks-libraries/overview) pour l'exécuter.
  </CodeGroupItem>

  ```python Python
  open_runs: dict[str, str] = {}  # workflow_run_id -> run name
  phase_names: dict[tuple[str, str], str] = {}  # (run ID, phase ID) -> phase name

  # Ouvrez d'abord le stream, puis envoyez le message utilisateur
  with client.beta.sessions.events.stream(session_id) as stream:
      client.beta.sessions.events.send(
          session_id,
          events=[
              {
                  "type": "user.message",
                  "content": [
                      {
                          "type": "text",
                          "text": "Which contracts in /contracts have a change-of-control clause?",
                      },
                  ],
              },
          ],
      )

      for event in stream:
          match event.type:
              case "workflow_run.created":
                  open_runs[event.workflow_run_id] = event.name
                  for phase in event.phases:
                      phase_names[event.workflow_run_id, phase.id] = phase.name
                  print(f"Run started: {event.name}")
              case "workflow_run.phase_started":
                  phase_id = event.workflow_run_phase_id
                  key = (event.workflow_run_id, phase_id)
                  print(f"  Phase: {phase_names.get(key, phase_id)}")
              case "workflow_run.status_ended":
                  name = open_runs.pop(event.workflow_run_id, event.workflow_run_id)
                  print(f"Run ended: {name} ({event.result.type})")
              case "agent.custom_tool_use":
                  # Répondez à l'arrivée de l'événement. Le thread d'une exécution peut attendre votre
                  # client pendant que la session reste en cours d'exécution.
                  result = call_tool(event.name, event.input)
                  try:
                      client.beta.sessions.events.send(
                          session_id,
                          events=[
                              {
                                  "type": "user.custom_tool_result",
                                  "custom_tool_use_id": event.id,
                                  "content": [{"type": "text", "text": result}],
                              },
                          ],
                      )
                  except anthropic.BadRequestError as error:
                      # Le serveur refuse un résultat qui arrive trop tard, après avoir
                      # archivé le thread de l'appel. Continuez à suivre l'exécution.
                      print(f"  Answer to {event.name} refused: {error.message}")
              case "session.status_idle":
                  # Terminé quand toutes les exécutions sont finies et que l'agent a terminé son tour
                  if not open_runs and event.stop_reason.type == "end_turn":
                      break
                  # Un état idle avec requires_action attend votre client, donc continuez la lecture.
                  # Pour toute autre raison d'arrêt, affichez-la et arrêtez.
                  if event.stop_reason.type not in ("end_turn", "requires_action"):
                      print(f"Session idle: {event.stop_reason.type}")
                      break
              case "session.status_terminated":
                  break
  ```

  ```typescript TypeScript
  const openRuns = new Map<string, string>(); // workflow_run_id -> run name
  const phaseNames = new Map<string, string>(); // "run ID:phase ID" -> phase name

  // Ouvrez d'abord le stream, puis envoyez le message utilisateur
  const stream = await client.beta.sessions.events.stream(sessionId);
  await client.beta.sessions.events.send(sessionId, {
    events: [
      {
        type: "user.message",
        content: [
          {
            type: "text",
            text: "Which contracts in /contracts have a change-of-control clause?"
          }
        ]
      }
    ]
  });

  events: for await (const event of stream) {
    switch (event.type) {
      case "workflow_run.created":
        openRuns.set(event.workflow_run_id, event.name);
        for (const phase of event.phases) {
          phaseNames.set(`${event.workflow_run_id}:${phase.id}`, phase.name);
        }
        console.log(`Run started: ${event.name}`);
        break;
      case "workflow_run.phase_started": {
        const phaseId = event.workflow_run_phase_id;
        const phaseName = phaseNames.get(`${event.workflow_run_id}:${phaseId}`);
        console.log(`  Phase: ${phaseName ?? phaseId}`);
        break;
      }
      case "workflow_run.status_ended": {
        const name = openRuns.get(event.workflow_run_id) ?? event.workflow_run_id;
        openRuns.delete(event.workflow_run_id);
        console.log(`Run ended: ${name} (${event.result.type})`);
        break;
      }
      case "agent.custom_tool_use": {
        // Répondez à l'arrivée de l'événement. Le thread d'une exécution peut attendre votre
        // client pendant que la session reste en cours.
        const result = await callTool(event.name, event.input);
        try {
          await client.beta.sessions.events.send(sessionId, {
            events: [
              {
                type: "user.custom_tool_result",
                custom_tool_use_id: event.id,
                content: [{ type: "text", text: result }]
              }
            ]
          });
        } catch (error) {
          // Le serveur refuse un résultat qui arrive trop tard, après avoir
          // archivé le thread de l'appel. Continuez à suivre l'exécution.
          if (!(error instanceof Anthropic.BadRequestError)) {
            throw error;
          }
          console.log(`  Answer to ${event.name} refused: ${error.message}`);
        }
        break;
      }
      case "session.status_idle": {
        // Terminé quand toutes les exécutions sont finies et que l'agent a fini son tour
        const reason = event.stop_reason.type;
        if (openRuns.size === 0 && reason === "end_turn") {
          break events;
        }
        // Un idle avec requires_action attend votre client, donc continuez la lecture.
        // Pour toute autre raison d'arrêt, affichez-la et arrêtez.
        if (reason !== "end_turn" && reason !== "requires_action") {
          console.log(`Session idle: ${reason}`);
          break events;
        }
        break;
      }
      case "session.status_terminated":
        break events;
    }
  }
  ```

  ```csharp C#
  var openRuns = new Dictionary<string, string>(); // workflow_run_id -> run name
  var phaseNames = new Dictionary<(string, string), string>(); // (run ID, phase ID) -> phase name

  // Ouvrez d'abord le stream, puis envoyez le message utilisateur. L'appel raw-response
  // ouvre le stream immédiatement. L'appel simple attendrait la première lecture.
  using var stream = await client.Beta.Sessions.Events.WithRawResponse.StreamStreaming(sessionId);
  await client.Beta.Sessions.Events.Send(sessionId, new()
  {
      Events =
      [
          new BetaManagedAgentsUserMessageEventParams
          {
              Type = BetaManagedAgentsUserMessageEventParamsType.UserMessage,
              Content =
              [
                  new BetaManagedAgentsTextBlock
                  {
                      Type = BetaManagedAgentsTextBlockType.Text,
                      Text = "Which contracts in /contracts have a change-of-control clause?",
                  },
              ],
          },
      ],
  });

  await foreach (var streamEvent in stream.Enumerate())
  {
      if (streamEvent.Value is BetaManagedAgentsWorkflowRunCreatedEvent created)
      {
          openRuns[created.WorkflowRunID] = created.Name;
          foreach (var listed in created.Phases)
          {
              phaseNames[(created.WorkflowRunID, listed.ID)] = listed.Name;
          }
          Console.WriteLine($"Run started: {created.Name}");
      }
      else if (streamEvent.Value is BetaManagedAgentsWorkflowRunPhaseStartedEvent phase)
      {
          var phaseId = phase.WorkflowRunPhaseID;
          var key = (phase.WorkflowRunID, phaseId);
          Console.WriteLine($"  Phase: {phaseNames.GetValueOrDefault(key, phaseId)}");
      }
      else if (streamEvent.Value is BetaManagedAgentsWorkflowRunStatusEndedEvent ended)
      {
          var name = openRuns.GetValueOrDefault(ended.WorkflowRunID, ended.WorkflowRunID);
          openRuns.Remove(ended.WorkflowRunID);
          var outcome = ended.Result.Value switch
          {
              BetaManagedAgentsWorkflowRunResultCompleted => "completed",
              BetaManagedAgentsWorkflowRunResultStopped => "stopped",
              BetaManagedAgentsWorkflowRunResultError => "error",
              _ => "ended",
          };
          Console.WriteLine($"Run ended: {name} ({outcome})");
      }
      else if (streamEvent.Value is BetaManagedAgentsAgentCustomToolUseEvent toolUse)
      {
          // Répondez à l'arrivée de l'événement. Le thread d'une exécution peut attendre votre
          // client tandis que la session reste en cours d'exécution.
          var result = await CallTool(toolUse.Name, toolUse.Input);
          try
          {
              await client.Beta.Sessions.Events.Send(sessionId, new()
              {
                  Events =
                  [
                      new BetaManagedAgentsUserCustomToolResultEventParams
                      {
                          Type = BetaManagedAgentsUserCustomToolResultEventParamsType.UserCustomToolResult,
                          CustomToolUseID = toolUse.ID,
                          Content =
                          [
                              new BetaManagedAgentsTextBlock
                              {
                                  Type = BetaManagedAgentsTextBlockType.Text,
                                  Text = result,
                              },
                          ],
                      },
                  ],
              });
          }
          catch (AnthropicBadRequestException error)
          {
              // Le serveur refuse un résultat qui arrive trop tard, après avoir
              // archivé le thread de l'appel. Continuez à suivre l'exécution.
              Console.WriteLine($"  Answer to {toolUse.Name} refused: {error.Message}");
          }
      }
      else if (streamEvent.Value is BetaManagedAgentsSessionStatusIdleEvent idle)
      {
          // Terminé lorsque toutes les exécutions sont finies et que l'agent a terminé son tour.
          var finished = idle.StopReason?.Value is BetaManagedAgentsSessionEndTurn;
          if (openRuns.Count == 0 && finished)
          {
              break;
          }
          // Un idle avec requires_action attend votre client, donc continuez la lecture.
          // Pour toute autre raison d'arrêt, affichez-la et arrêtez.
          if (!finished && idle.StopReason?.Value is not BetaManagedAgentsSessionRequiresAction)
          {
              var reason = idle.StopReason?.Json.GetProperty("type").GetString();
              Console.WriteLine($"Session idle: {reason}");
              break;
          }
      }
      else if (streamEvent.Value is BetaManagedAgentsSessionStatusTerminatedEvent)
      {
          break;
      }
  }
  ```

  ```go Go
  	openRuns := map[string]string{}      // workflow_run_id -> run name
  	phaseNames := map[[2]string]string{} // {run ID, phase ID} -> phase name

  	// Ouvrez d'abord le stream, puis envoyez le message utilisateur
  	stream := client.Beta.Sessions.Events.StreamEvents(ctx, sessionID, anthropic.BetaSessionEventStreamParams{})
  	defer stream.Close()

  	if _, err := client.Beta.Sessions.Events.Send(ctx, sessionID, anthropic.BetaSessionEventSendParams{
  		Events: []anthropic.BetaManagedAgentsEventParamsUnion{{
  			OfUserMessage: &anthropic.BetaManagedAgentsUserMessageEventParams{
  				Type: anthropic.BetaManagedAgentsUserMessageEventParamsTypeUserMessage,
  				Content: []anthropic.BetaManagedAgentsUserMessageEventParamsContentUnion{{
  					OfText: &anthropic.BetaManagedAgentsTextBlockParam{
  						Type: anthropic.BetaManagedAgentsTextBlockTypeText,
  						Text: "Which contracts in /contracts have a change-of-control clause?",
  					},
  				}},
  			},
  		}},
  	}); err != nil {
  		panic(err)
  	}

  events:
  	for stream.Next() {
  		switch event := stream.Current().AsAny().(type) {
  		case anthropic.BetaManagedAgentsWorkflowRunCreatedEvent:
  			openRuns[event.WorkflowRunID] = event.Name
  			for _, phase := range event.Phases {
  				phaseNames[[2]string{event.WorkflowRunID, phase.ID}] = phase.Name
  			}
  			fmt.Printf("Run started: %s\n", event.Name)
  		case anthropic.BetaManagedAgentsWorkflowRunPhaseStartedEvent:
  			phaseName, ok := phaseNames[[2]string{event.WorkflowRunID, event.WorkflowRunPhaseID}]
  			if !ok {
  				phaseName = event.WorkflowRunPhaseID
  			}
  			fmt.Printf("  Phase: %s\n", phaseName)
  		case anthropic.BetaManagedAgentsWorkflowRunStatusEndedEvent:
  			name, ok := openRuns[event.WorkflowRunID]
  			if !ok {
  				name = event.WorkflowRunID
  			}
  			delete(openRuns, event.WorkflowRunID)
  			fmt.Printf("Run ended: %s (%s)\n", name, event.Result.Type)
  		case anthropic.BetaManagedAgentsAgentCustomToolUseEvent:
  			// Répondez à l'arrivée de l'événement. Le thread d'une exécution peut attendre votre
  			// client pendant que la session reste en cours d'exécution.
  			result := callTool(event.Name, event.Input)
  			if _, err := client.Beta.Sessions.Events.Send(ctx, sessionID, anthropic.BetaSessionEventSendParams{
  				Events: []anthropic.BetaManagedAgentsEventParamsUnion{{
  					OfUserCustomToolResult: &anthropic.BetaManagedAgentsUserCustomToolResultEventParams{
  						Type:            anthropic.BetaManagedAgentsUserCustomToolResultEventParamsTypeUserCustomToolResult,
  						CustomToolUseID: event.ID,
  						Content: []anthropic.BetaManagedAgentsUserCustomToolResultEventParamsContentUnion{{
  							OfText: &anthropic.BetaManagedAgentsTextBlockParam{
  								Type: anthropic.BetaManagedAgentsTextBlockTypeText,
  								Text: result,
  							},
  						}},
  					},
  				}},
  			}); err != nil {
  				// Le serveur refuse un résultat qui arrive trop tard, après avoir
  				// archivé le thread de l'appel. Continuez à suivre l'exécution.
  				var apiErr *anthropic.Error
  				if !errors.As(err, &apiErr) || apiErr.StatusCode != http.StatusBadRequest {
  					panic(err)
  				}
  				fmt.Printf("  Answer to %s refused: %v\n", event.Name, err)
  			}
  		case anthropic.BetaManagedAgentsSessionStatusIdleEvent:
  			// Terminé quand toutes les exécutions sont finies et que l'agent a terminé son tour
  			switch event.StopReason.AsAny().(type) {
  			case anthropic.BetaManagedAgentsSessionEndTurn:
  				if len(openRuns) == 0 {
  					break events
  				}
  			// Un état idle avec requires_action attend votre client, donc continuez la lecture.
  			// Pour toute autre raison d'arrêt, affichez-la et arrêtez.
  			case anthropic.BetaManagedAgentsSessionRequiresAction:
  			default:
  				fmt.Printf("Session idle: %s\n", event.StopReason.Type)
  				break events
  			}
  		case anthropic.BetaManagedAgentsSessionStatusTerminatedEvent:
  			break events
  		}
  	}
  	if err := stream.Err(); err != nil {
  		panic(err)
  	}
  ```

  ```java Java
  var openRuns = new HashMap<String, String>(); // workflow_run_id -> run name
  var phaseNames = new HashMap<String, String>(); // "run ID:phase ID" -> phase name

  // Ouvrez d'abord le stream, puis envoyez le message utilisateur
  try (var stream = client.beta().sessions().events().streamStreaming(sessionId)) {
      client.beta().sessions().events().send(
          sessionId,
          EventSendParams.builder()
              .addEvent(BetaManagedAgentsUserMessageEventParams.builder()
                  .type(BetaManagedAgentsUserMessageEventParams.Type.USER_MESSAGE)
                  .addTextContent("Which contracts in /contracts have a change-of-control clause?")
                  .build())
              .build()
      );

      Iterable<BetaManagedAgentsStreamSessionEvents> events = stream.stream()::iterator;
      events:
      for (var event : events) {
          switch (event.type().value()) {
              case WORKFLOW_RUN_CREATED -> {
                  var created = event.asWorkflowRunCreated();
                  openRuns.put(created.workflowRunId(), created.name());
                  created.phases().forEach(phase ->
                      phaseNames.put(created.workflowRunId() + ":" + phase.id(), phase.name()));
                  IO.println("Run started: " + created.name());
              }
              case WORKFLOW_RUN_PHASE_STARTED -> {
                  var started = event.asWorkflowRunPhaseStarted();
                  var phaseId = started.workflowRunPhaseId();
                  var key = started.workflowRunId() + ":" + phaseId;
                  IO.println("  Phase: " + phaseNames.getOrDefault(key, phaseId));
              }
              case WORKFLOW_RUN_STATUS_ENDED -> {
                  var ended = event.asWorkflowRunStatusEnded();
                  var name = openRuns.getOrDefault(ended.workflowRunId(), ended.workflowRunId());
                  openRuns.remove(ended.workflowRunId());
                  var result = ended.result();
                  var outcome = result.isCompleted() ? "completed"
                      : result.isStopped() ? "stopped"
                      : result.isError() ? "error"
                      : "ended";
                  IO.println("Run ended: " + name + " (" + outcome + ")");
              }
              case AGENT_CUSTOM_TOOL_USE -> {
                  // Répondez à l'arrivée de l'événement. Le thread d'une exécution peut attendre votre
                  // client pendant que la session reste en cours.
                  var toolUse = event.asAgentCustomToolUse();
                  var result = callTool(toolUse.name(), toolUse.input());
                  try {
                      client.beta().sessions().events().send(
                          sessionId,
                          EventSendParams.builder()
                              .addEvent(BetaManagedAgentsUserCustomToolResultEventParams.builder()
                                  .type(BetaManagedAgentsUserCustomToolResultEventParams.Type.USER_CUSTOM_TOOL_RESULT)
                                  .customToolUseId(toolUse.id())
                                  .addTextContent(result)
                                  .build())
                              .build());
                  } catch (BadRequestException e) {
                      // Le serveur refuse un résultat qui arrive trop tard, après avoir
                      // archivé le thread de l'appel. Continuez à suivre l'exécution.
                      IO.println("  Answer to " + toolUse.name() + " refused: " + e.getMessage());
                  }
              }
              case SESSION_STATUS_IDLE -> {
                  // Terminé quand toutes les exécutions sont finies et que l'agent a fini son tour
                  var stopReason = event.asSessionStatusIdle().stopReason();
                  if (openRuns.isEmpty() && stopReason.isEndTurn()) {
                      break events;
                  }
                  // Un idle avec requires_action attend votre client, donc continuez la lecture.
                  // Pour toute autre raison d'arrêt, affichez-la et arrêtez.
                  if (!stopReason.isEndTurn() && !stopReason.isRequiresAction()) {
                      IO.println("Session idle: " + stopReason.type().asString());
                      break events;
                  }
              }
              case SESSION_STATUS_TERMINATED -> {
                  break events;
              }
              default -> {}
          }
      }
  }
  ```

  ```php PHP
  $openRuns = []; // workflow_run_id => run name
  $phaseNames = []; // run ID => [phase ID => phase name]

  // Ouvrez d'abord le stream, puis envoyez le message utilisateur
  $stream = $client->beta->sessions->events->streamStream($sessionId);
  $client->beta->sessions->events->send(
      $sessionId,
      events: [
          [
              'type' => 'user.message',
              'content' => [
                  ['type' => 'text', 'text' => 'Which contracts in /contracts have a change-of-control clause?'],
              ],
          ],
      ],
  );

  foreach ($stream as $event) {
      switch (true) {
          case $event instanceof \Anthropic\Beta\Sessions\Events\ManagedAgentsWorkflowRunCreatedEvent:
              $openRuns[$event->workflowRunID] = $event->name;
              foreach ($event->phases as $phase) {
                  $phaseNames[$event->workflowRunID][$phase->id] = $phase->name;
              }
              echo "Run started: {$event->name}", PHP_EOL;
              break;
          case $event instanceof \Anthropic\Beta\Sessions\Events\ManagedAgentsWorkflowRunPhaseStartedEvent:
              $phaseId = $event->workflowRunPhaseID;
              echo '  Phase: ', $phaseNames[$event->workflowRunID][$phaseId] ?? $phaseId, PHP_EOL;
              break;
          case $event instanceof \Anthropic\Beta\Sessions\Events\ManagedAgentsWorkflowRunStatusEndedEvent:
              $name = $openRuns[$event->workflowRunID] ?? $event->workflowRunID;
              unset($openRuns[$event->workflowRunID]);
              echo "Run ended: {$name} ({$event->result->type})", PHP_EOL;
              break;
          case $event instanceof \Anthropic\Beta\Sessions\Events\ManagedAgentsAgentCustomToolUseEvent:
              // Répondez quand l'événement arrive. Le thread d'une exécution peut attendre votre
              // client pendant que la session reste en cours.
              $result = callTool($event->name, $event->input);
              try {
                  $client->beta->sessions->events->send(
                      $sessionId,
                      events: [
                          [
                              'type' => 'user.custom_tool_result',
                              'custom_tool_use_id' => $event->id,
                              'content' => [['type' => 'text', 'text' => $result]],
                          ],
                      ],
                  );
              } catch (\Anthropic\Core\Exceptions\BadRequestException $error) {
                  // Le serveur refuse un résultat qui arrive trop tard, après avoir
                  // archivé le thread de l'appel. Continuez à suivre l'exécution.
                  echo "  Answer to {$event->name} refused: {$error->getMessage()}", PHP_EOL;
              }
              break;
          case $event instanceof \Anthropic\Beta\Sessions\Events\ManagedAgentsSessionStatusIdleEvent:
              // Terminé quand toutes les exécutions sont finies et que l'agent a terminé son tour
              $finished = $event->stopReason->type === 'end_turn';
              if (!$openRuns && $finished) {
                  break 2;
              }
              // Un état idle avec requires_action attend votre client, donc continuez la lecture.
              // Pour toute autre raison d'arrêt, affichez-la et arrêtez.
              if (!$finished && $event->stopReason->type !== 'requires_action') {
                  echo "Session idle: {$event->stopReason->type}", PHP_EOL;
                  break 2;
              }
              break;
          case $event instanceof \Anthropic\Beta\Sessions\Events\ManagedAgentsSessionStatusTerminatedEvent:
              break 2;
      }
  }
  $stream->close();
  ```

  ```ruby Ruby
  open_runs = {} # workflow_run_id => run name
  phase_names = {} # [run ID, phase ID] => phase name

  # Ouvrez d'abord le stream, puis envoyez le message utilisateur
  stream = client.beta.sessions.events.stream_events(session_id)

  client.beta.sessions.events.send_(
    session_id,
    events: [{
      type: "user.message",
      content: [{type: "text", text: "Which contracts in /contracts have a change-of-control clause?"}]
    }]
  )

  stream.each do |event|
    case event
    when Anthropic::Beta::Sessions::BetaManagedAgentsWorkflowRunCreatedEvent
      open_runs[event.workflow_run_id] = event.name
      event.phases.each { |phase| phase_names[[event.workflow_run_id, phase.id]] = phase.name }
      puts "Run started: #{event.name}"
    when Anthropic::Beta::Sessions::BetaManagedAgentsWorkflowRunPhaseStartedEvent
      phase_id = event.workflow_run_phase_id
      puts "  Phase: #{phase_names.fetch([event.workflow_run_id, phase_id], phase_id)}"
    when Anthropic::Beta::Sessions::BetaManagedAgentsWorkflowRunStatusEndedEvent
      name = open_runs.delete(event.workflow_run_id) || event.workflow_run_id
      puts "Run ended: #{name} (#{event.result.type})"
    when Anthropic::Beta::Sessions::BetaManagedAgentsAgentCustomToolUseEvent
      # Répondez à l'arrivée de l'événement. Le thread d'une exécution peut attendre votre
      # client pendant que la session reste en cours.
      result = call_tool.call(event.name, event.input)
      begin
        client.beta.sessions.events.send_(
          session_id,
          events: [{
            type: "user.custom_tool_result",
            custom_tool_use_id: event.id,
            content: [{type: "text", text: result}]
          }]
        )
      rescue Anthropic::Errors::BadRequestError => error
        # Le serveur refuse un résultat qui arrive trop tard, après avoir archivé
        # le thread de l'appel. Continuez à suivre l'exécution.
        puts "  Answer to #{event.name} refused: #{error.message}"
      end
    when Anthropic::Beta::Sessions::BetaManagedAgentsSessionStatusIdleEvent
      reason = event.stop_reason.type.to_sym
      # Terminé quand toutes les exécutions sont finies et que l'agent a fini son tour
      break if open_runs.empty? && reason == :end_turn
      # Un idle avec requires_action attend votre client, donc continuez la lecture.
      # Pour toute autre raison d'arrêt, affichez-la et arrêtez.
      unless %i[end_turn requires_action].include?(reason)
        puts "Session idle: #{reason}"
        break
      end
    when Anthropic::Beta::Sessions::BetaManagedAgentsSessionStatusTerminatedEvent
      break
    end
  end
  ```
</CodeGroup>

## Interrompre une session avec des exécutions ouvertes

Envoyez [`user.interrupt`](https://platform.claude.com/docs/fr/managed-agents/events-and-streaming#interrupt-the-agent) sans `session_thread_id` (java: `sessionThreadId`; php: `sessionThreadID`; csharp, go: `SessionThreadID`), ou avec l'ID du fil principal. Cela arrête le tour de l'agent. Cela ne met fin à aucune exécution. Les exécutions de la session peuvent se mettre en pause ou continuer à s'exécuter, et leurs événements peuvent ne pas indiquer lequel des deux. La durée de vie d'une exécution en pause continue de s'écouler ; l'exécution peut donc se terminer avec `timeout_error` pendant qu'elle est en pause.

* **Appels d'outils en attente :** Après l'interruption, un appel d'outil d'un fil d'exécution peut encore attendre votre client. Répondez à chacun d'eux. Pour annuler un appel qui demande une confirmation, refusez-le. Pour annuler un appel d'outil personnalisé, envoyez un résultat avec `is_error` (java, php: `isError`; csharp, go: `IsError`) défini sur `true` et un texte dans `content` (csharp, go: `Content`) qui en explique la raison. Tant que la session est `idle` avec `requires_action`, un `user.message` renvoie 400 ; répondez donc d'abord aux appels.
* **Pour arrêter les exécutions :** Envoyez un `user.message` demandant à l'agent d'arrêter ses exécutions. Une exécution arrêtée se termine avec le `result` `{"type": "stopped"}`. Tant que la session est `idle` avec `budget_reached`, un `user.message` renvoie 400 jusqu'à ce que vous augmentiez ou supprimiez le budget. L'augmenter ou le supprimer fait aussi reprendre les exécutions que le budget a mises en pause, sauf si l'interruption les a aussi mises en pause.
* **Pour continuer :** Envoyez un `user.message` demandant à l'agent de poursuivre ses exécutions. Après une interruption, les exécutions peuvent attendre ce message. Si la session est `idle` avec `budget_reached`, augmentez ou supprimez d'abord le budget.
* **Résultats des exécutions :** Une exécution qui se termine après l'interruption envoie quand même `workflow_run.status_ended`.

## Pendant qu'une exécution est ouverte

| Requête                                                                                      | Pendant qu'une exécution est ouverte                                                                                                                                                                                                                                                                                                               | Que faire                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| -------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Archiver ou supprimer la session                                                             | Peut renvoyer 400 tant qu'une exécution est ouverte, quel que soit le statut de la session. Le `error.details.error_code` de l'erreur peut être `"workflow_run_open"`. Peut aussi réussir.                                                                                                                                                         | Demandez à l'agent d'arrêter ses exécutions, ou attendez que chaque exécution soit terminée. Une exécution en pause ne se termine d'elle-même que lorsque sa durée de vie s'écoule. Envoyez ensuite la requête une fois la session `idle`. Un archivage qui réussit met fin à chaque exécution ouverte avec `{"type": "stopped"}`. Après un archivage, le `workflow_run.status_ended` d'une exécution, et le `workflow_run.phase_ended` d'une phase encore ouverte, n'arrivent pas sur le flux. Listez les événements de la session pour les lire. Après une suppression qui réussit, aucun événement `workflow_run` ne signale la fin des exécutions de la session.                                                                                                                                                                                                                                                                                                                                    |
| Archiver l'un des fils d'une exécution                                                       | Renvoie 400 avec `error.details.error_code: "workflow_run_open"` tant que l'exécution est ouverte, en cours ou inactive, sauf si le serveur a déjà archivé le fil.                                                                                                                                                                                 | Rien. Le serveur archive les fils d'une exécution.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| Mettre à jour l'`agent` (csharp, go: `Agent`) de la session                                  | Renvoie 400 avec `error.details.error_code: "workflow_run_open"` tant qu'une exécution est ouverte, même en pause. La mise à jour de l'agent sous-jacent est toujours acceptée, et la session conserve sa propre copie. Une requête qui envoie également d'autres champs, comme `budget` (csharp, go: `Budget`), est rejetée dans son intégralité. | Attendez que chaque exécution ait son `workflow_run.status_ended`, ou demandez à l'agent d'arrêter ses exécutions.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| Répondre à un appel d'outil ou à une confirmation d'outil provenant d'un fil d'une exécution | Autorisé. Il arrive sur le flux principal, et son `session_thread_id` (java: `sessionThreadId`; php: `sessionThreadID`; csharp, go: `SessionThreadID`) nomme le fil.                                                                                                                                                                               | Répondez dès que l'événement arrive, en transmettant l'`id` (csharp, go: `ID`) de l'événement comme `tool_use_id` (java: `toolUseId`; php: `toolUseID`; csharp, go: `ToolUseID`) ou `custom_tool_use_id` (java: `customToolUseId`; php: `customToolUseID`; csharp, go: `CustomToolUseID`). N'attendez pas `session.status_idle` : la session peut rester `running` pendant que les autres fils de l'exécution travaillent. Une fois que le serveur a archivé le fil, un résultat d'outil pour l'un de ses appels n'a aucun effet, et il peut renvoyer 400. Lorsqu'un résultat d'outil renvoie 400, trouvez le fil de l'appel dans la liste des fils. Si son statut est `terminated`, le résultat est arrivé trop tard ; abandonnez-le donc. Envoyez chaque résultat d'outil dans une requête distincte, car le serveur refuse une requête entière lorsqu'il refuse l'un de ses événements. Une confirmation d'outil qui arrive trop tard renvoie 200, ce qui ne signifie pas que l'outil s'est exécuté. |

### Reconstruire l'état des exécutions après la reconnexion

Reconstruisez l'état de chaque exécution à partir des événements de la session. Le flux ne rejoue pas ce que vous avez manqué : une nouvelle connexion ne délivre que les événements émis après son ouverture. Listez donc les événements avec un filtre `types` (curl: `types[]`; cli: `--type`; csharp, go: `Types`), avec une entrée pour chaque type d'événement, comme dans [Lister les événements passés](https://platform.claude.com/docs/fr/managed-agents/events-and-streaming#list-past-events). Passez le `next_page` (ruby: `next_page_`; java, php: `nextPage`; csharp, go: `NextPage`) de chaque réponse comme `page` (cli: `--page`; csharp, go: `Page`) jusqu'à ce que `next_page` (ruby: `next_page_`; java, php: `nextPage`; csharp, go: `NextPage`) soit `null` (python: `None`; ruby: `nil`; java: `Optional.empty()`; go: `""`) ou absent. `workflow_run.created`, `workflow_run.status_running`, `workflow_run.status_idle` et `workflow_run.status_ended` donnent l'état de chaque exécution, à ceci près qu'une exécution mise en pause après une interruption peut encore apparaître comme en cours. `workflow_run.phase_started` et `workflow_run.phase_ended` permettent de reconstruire la progression. Une exécution sans aucun événement de statut n'a pas encore commencé à s'exécuter. Aucun endpoint ne liste les exécutions.

## Coût et budgets

Les requêtes au modèle d'une exécution comptent dans le [budget de la session](https://platform.claude.com/docs/fr/managed-agents/budgets). Une exécution n'a pas de prix propre. Les tokens que ses agents utilisent sont facturés comme les autres tokens de la session, aux tarifs de chaque modèle. Pour l'ensemble des frais d'une session, consultez [Tarification de Claude Managed Agents](https://platform.claude.com/docs/fr/about-claude/pricing#claude-managed-agents-pricing).

* **Utilisation d'une exécution :** Listez les fils de la session et additionnez les nombres de tokens dans `usage` (csharp, go: `Usage`) des fils portant le `workflow_run_id` (java: `workflowRunId`; php: `workflowRunID`; csharp, go: `WorkflowRunID`) de l'exécution. La liste inclut les fils archivés, dont le statut est `terminated`, de sorte que les fils d'une exécution terminée sont comptabilisés. Passez le `next_page` (ruby: `next_page_`; java, php: `nextPage`; csharp, go: `NextPage`) de chaque réponse comme `page` (cli: `--page`; csharp, go: `Page`) jusqu'à ce que `next_page` (ruby: `next_page_`; java, php: `nextPage`; csharp, go: `NextPage`) soit `null` (python: `None`; ruby: `nil`; java: `Optional.empty()`; go: `""`) ou absent, et ignorez tout fil sans `usage` (csharp, go: `Usage`). Si vous additionnez plutôt le `list_cost` (java, php: `listCost`; csharp, go: `ListCost`) des fils, le total exclut le temps d'exécution de la session, et chaque montant est arrondi séparément.
* **Au budget :** Chaque exécution ouverte se met en pause, et la session signale `idle` avec `budget_reached`, ou `requires_action` si un appel d'outil est également en attente. Chaque fil termine la requête de modèle qu'il a déjà commencée, de sorte qu'une exécution peut dépasser le budget d'une requête par fil actif. Augmenter ou supprimer le budget fait reprendre les exécutions qu'il a mises en pause, sauf si une interruption les a également mises en pause. Si l'utilisation de la session inclut un modèle sans tarif public, seule la suppression du budget le fait ; consultez [Modèles sans tarif public](https://platform.claude.com/docs/fr/managed-agents/budgets#models-without-a-list-price). La durée de vie d'une exécution en pause continue de s'écouler, de sorte que l'exécution peut se terminer avec `timeout_error` pendant qu'elle est en pause.

## Limites

| Limite                                                                 | Valeur                                                       | À la limite                                                                                                                                                                                                                                                       |
| ---------------------------------------------------------------------- | ------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Fils actifs simultanément dans une exécution                           | 64                                                           | L'exécution n'en crée plus jusqu'à ce que l'un d'eux se termine. L'API ne garantit pas ce nombre.                                                                                                                                                                 |
| Agents qu'un workflow démarre sur toute la durée de vie de l'exécution | 1 000                                                        | Lorsque le workflow en demande davantage, le serveur ne démarre pas d'autre agent, et l'exécution se termine avec `thread_limit_error`. Le serveur peut réexécuter un agent en échec sur un nouveau fil, de sorte qu'une exécution peut avoir plus de 1 000 fils. |
| Durée de vie d'une exécution                                           | 24 heures par défaut, ou la durée de vie que l'agent définit | L'exécution se termine avec `timeout_error`. Aucun événement n'indique quelle durée de vie l'agent a définie.                                                                                                                                                     |
| Exécutions ouvertes simultanément dans une session                     | 10 par défaut                                                | Le serveur refuse de démarrer une autre exécution. L'appel d'outil de l'agent reçoit une erreur, et vous recevez un `workflow_run.error` dont l'`error.type` est `max_workflow_runs_error`. Les exécutions inactives comptent dans la limite.                     |

Le serveur raccourcit le `name` (csharp, go: `Name`) d'une exécution ou d'une phase à 64 caractères et sa `description` (csharp, go: `Description`) à 256. Le serveur applique aux workflows d'autres limites, ainsi que des règles, qui ne sont pas listées ici. Ce que vous voyez dépend du moment où le serveur détecte le problème :

| Ce qui se passe                                                                                             | Ce que vous voyez                                                                                   |
| ----------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- |
| Le workflow dépasse l'une des autres limites lorsque l'agent démarre l'exécution                            | Le démarrage est refusé. Vous recevez un `workflow_run.error`, et aucune exécution.                 |
| L'exécution dépasse l'une des autres limites plus tard                                                      | Vous recevez un `workflow_run.error`, puis l'exécution peut se terminer avec `unknown_error`.       |
| Le serveur détecte après le démarrage que le workflow enfreint une règle des workflows, autre qu'une limite | Vous recevez un `workflow_run.error`, et l'exécution peut ensuite se terminer avec `program_error`. |

Une session peut démarrer un nombre quelconque d'exécutions au cours de sa vie.

* **Démarrage des exécutions :** Seul l'agent du fil principal de la session démarre des exécutions. Un agent travaillant dans le fil d'une exécution ne peut pas démarrer sa propre exécution, de sorte que les exécutions ne s'imbriquent pas.
* **Fin des exécutions :** Aucun événement que vous envoyez ne met fin à une exécution ; l'archivage de la session le peut.
* **Les fils d'une exécution :** Vous ne pouvez pas [en arrêter un par son ID](https://platform.claude.com/docs/fr/managed-agents/session-threads#interrupt-a-session-thread), ni en archiver un tant que son exécution est ouverte.
* **Limite de fils :** Les fils d'une exécution sont exemptés de la [limite de fils enfants](https://platform.claude.com/docs/fr/managed-agents/session-threads#primary-thread-and-session-threads) de la session.
* **Visibilité :** Vous ne voyez pas le code du workflow, mais vous pouvez demander le workflow à l'agent, comme le décrit l'astuce dans [Les fils d'une exécution](https://platform.claude.com/docs/fr/managed-agents/workflow-runs#a-runs-threads). Vous ne voyez pas non plus les appels d'outils que l'agent effectue pour démarrer et gérer les exécutions, ni le résultat que chaque fil renvoie au workflow.

### Limites de débit

Le travail d'une exécution est comptabilisé dans les « rate limits » (limites de débit) dont votre organisation dispose déjà.

| Quoi                                                                                           | Compte dans                                                                                                                                                       | Que faire                                                                                                                                                                       |
| ---------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Les requêtes de votre client pour récupérer ou lister la session, ses fils et leurs événements | La limite de lecture des [endpoints Managed Agents](https://platform.claude.com/docs/fr/managed-agents/reference#rate-limits)                                     | Suivez une exécution sur le flux d'événements de la session plutôt que par interrogation périodique.                                                                            |
| Les requêtes au modèle provenant des fils d'une exécution                                      | Vos [limites de débit de l'API Messages](https://platform.claude.com/docs/fr/api/rate-limits) pour le modèle qu'utilise chaque fil, avec le reste de votre trafic | Prévoyez de la marge pour une exécution dans ces limites, ou [demandez des limites plus élevées](https://platform.claude.com/docs/fr/api/rate-limits#requesting-higher-limits). |

Lorsqu'une requête au modèle provenant de l'un des fils d'une exécution est soumise à une limite de débit, ou que le modèle est surchargé, le flux propre du fil peut recevoir un `session.error` de type `model_rate_limited_error` ou `model_overloaded_error` :

* Si son `retry_status.type` vaut `retrying`, le serveur retente la requête, et le fil travaille toujours.
* S'il vaut `exhausted`, le fil a échoué. Si le workflow laisse cet échec mettre fin à l'exécution, l'exécution se termine avec `program_error`, qui n'en nomme pas la cause. Lisez les événements des fils en échec pour la trouver.

Le serveur limite aussi la quantité de travail que l'ensemble des sessions de votre organisation effectuent chaque minute. Un fil qui atteint cette limite s'arrête, avec un `session.error` sur son propre flux dont le message mentionne une limite de débit. Attendez une minute avant de demander à l'agent de continuer.
