---
title: Fils de session
url: https://platform.claude.com/docs/fr/managed-agents/session-threads
description: Listez, interrompez et archivez les fils d'une session multi-agents, lisez leurs événements et gérez les autorisations d'outils entre eux.
featureMetadata:
  topic:
    title: Managed Agents
    url: https://platform.claude.com/docs/en/managed-agents/overview
  status: beta
  betaHeader: managed-agents-2026-04-01
---

Dans une [session multi-agents](https://platform.claude.com/docs/fr/managed-agents/multiagent-orchestration), chaque agent travaille dans son propre **« session thread » (fil de session)**. Cette page explique comment lister, interrompre et archiver les fils, les événements qu'ils envoient et le fonctionnement des autorisations d'outils entre eux. Une [exécution de workflow](https://platform.claude.com/docs/fr/managed-agents/workflow-runs) crée également des fils de session.

## Fil principal et fils de session

Le **« session-level event stream » (flux d'événements au niveau de la session)**, à l'adresse `/v1/sessions/{session_id}/events/stream`, est considéré comme le **« primary thread » (fil principal)**, qui contient une vue condensée de toute l'activité de tous les fils. Vous ne voyez pas l'activité complète des [sous-agents](https://platform.claude.com/docs/fr/managed-agents/multiagent-orchestration#delegate-to-subagents), mais vous voyez le début et la fin de leur travail, ainsi que les événements bloquants tels que les demandes d'autorisation d'outils.

Les **fils de session** sont l'endroit où vous examinez en détail l'activité d'un agent spécifique.

Le `status` (csharp, go: `Status`) de la session est une agrégation de toute l'activité des agents ; si au moins un fil est `running`, alors le statut global de la session est également `running`. Une [exécution de workflow](https://platform.claude.com/docs/fr/managed-agents/workflow-runs) en cours peut également maintenir la session à `running`, même lorsqu'aucun de ses fils ne travaille. Lorsqu'aucun fil ne travaille et qu'un fil attend votre client, la session est `idle` ; consultez [Savoir quand le travail est terminé](https://platform.claude.com/docs/fr/managed-agents/workflow-runs#know-when-the-work-is-done).

Un [budget de session](https://platform.claude.com/docs/fr/managed-agents/budgets) est un plafond unique partagé entre tous les fils d'une session. Lorsque le plafond est atteint, les fils se mettent en pause indépendamment, et le coût de chaque fil est calculé selon le modèle servi propre à ce fil.

<Note>
  Une session peut avoir au maximum 25 fils enfants à la fois. Les fils inactifs sont comptabilisés jusqu'à ce que vous les archiviez, et le fil principal n'est pas comptabilisé. L'agent du fil principal peut appeler plusieurs copies d'un même sous-agent, créant ainsi plusieurs fils associés à un seul `agent`. Les fils de consultation du [conseiller](https://platform.claude.com/docs/fr/managed-agents/multiagent-orchestration#give-the-session-an-advisor) sont exemptés de cette limite. Il en va de même pour les fils d'une [exécution de workflow](https://platform.claude.com/docs/fr/managed-agents/workflow-runs).
</Note>

## Lister les fils

Listez tous les fils associés à une session comme suit :

<CodeGroup>
  ```bash cURL
  curl -fsS "https://api.anthropic.com/v1/sessions/$SESSION_ID/threads" \
    -H "x-api-key: $ANTHROPIC_API_KEY" \
    -H "anthropic-version: 2023-06-01" \
    -H "anthropic-beta: managed-agents-2026-04-01"
  ```

  ```bash CLI
  ant beta:sessions:threads list --session-id "$SESSION_ID"
  ```

  ```python Python
  for thread in client.beta.sessions.threads.list(session.id):
      agent = thread.agent
      label = agent.type if agent.type == "advisor" else agent.name
      print(f"[{label}] {thread.status}")
  ```

  ```typescript TypeScript
  for await (const thread of client.beta.sessions.threads.list(session.id)) {
    const label = thread.agent.type === "advisor" ? thread.agent.type : thread.agent.name;
    console.log(`[${label}] ${thread.status}`);
  }
  ```

  ```csharp C#
  await foreach (var sessionThread in (await client.Beta.Sessions.Threads.List(session.ID)).Paginate())
  {
      var label = sessionThread.Agent.Name ?? sessionThread.Agent.Json.GetProperty("type").GetString();
      Console.WriteLine($"[{label}] {sessionThread.Status.Raw()}");
  }
  ```

  ```go Go
  threads := client.Beta.Sessions.Threads.ListAutoPaging(ctx, session.ID, anthropic.BetaSessionThreadListParams{})
  for threads.Next() {
  	thread := threads.Current()
  	label := cmp.Or(thread.Agent.Name, thread.Agent.Type)
  	fmt.Printf("[%s] %s\n", label, thread.Status)
  }
  if err := threads.Err(); err != nil {
  	panic(err)
  }
  ```

  ```java Java
  for (var thread : client.beta().sessions().threads().list(session.id()).autoPager()) {
      var agent = thread.agent();
      var label = agent.isAgent() ? agent.asAgent().name() : agent.type().asString();
      IO.println("[" + label + "] " + thread.status());
  }
  ```

  ```php PHP
  foreach ($client->beta->sessions->threads->list($session->id)->pagingEachItem() as $thread) {
      $label = $thread->agent instanceof \Anthropic\Beta\Agents\BetaManagedAgentsAdvisor
          ? $thread->agent->type
          : $thread->agent->name;
      echo "[{$label}] {$thread->status}\n";
  }
  ```

  ```ruby Ruby
  client.beta.sessions.threads.list(session.id).auto_paging_each do |thread|
    agent = thread.agent
    label = agent.type == :advisor ? agent.type : agent.name
    puts "[#{label}] #{thread.status}"
  end
  ```
</CodeGroup>

La liste complète inclut le fil principal. `parent_thread_id` (java: `parentThreadId`; php: `parentThreadID`; csharp, go: `ParentThreadID`) vaut `null` (python: `None`; ruby: `nil`; java: `Optional.empty()`; go: `""`) pour le fil principal. Tous les autres fils sont des fils enfants. `workflow_run_id` (java: `workflowRunId`; php: `workflowRunID`; csharp, go: `WorkflowRunID`) vaut `null` (python: `None`; ruby: `nil`; java: `Optional.empty()`; go: `""`) sauf sur [les fils d'une exécution](https://platform.claude.com/docs/fr/managed-agents/workflow-runs#a-runs-threads).

Pour lister uniquement les fils ayant certains statuts, ajoutez `statuses[]` à la requête, et répétez-le pour indiquer plusieurs statuts, comme dans `?statuses[]=running&statuses[]=idle`. Omettez-le pour renvoyer les fils de tous les statuts.

## Interrompre un fil de session

Envoyez `user.interrupt` avec `session_thread_id` (java: `sessionThreadId`; php: `sessionThreadID`; csharp, go: `SessionThreadID`) pour arrêter un fil spécifique. Omettre `session_thread_id` (java: `sessionThreadId`; php: `sessionThreadID`; csharp, go: `SessionThreadID`) interrompt tous les fils non archivés de la session, y compris le fil principal. Dans une session avec des workflows dynamiques, une interruption ne met fin à aucune exécution, et une interruption qui désigne un fil d'exécution n'arrête rien. Une interruption clôt les appels d'outils en attente des autres fils enfants, mais ne comptez pas sur elle pour clore ceux d'un fil d'exécution. Consultez [Interrompre une session avec des exécutions ouvertes](https://platform.claude.com/docs/fr/managed-agents/workflow-runs#interrupt-a-session-with-runs-open).

<CodeGroup>
  ```bash cURL
  curl -fsS "https://api.anthropic.com/v1/sessions/$SESSION_ID/events?beta=true" \
    -H "x-api-key: $ANTHROPIC_API_KEY" \
    -H "anthropic-version: 2023-06-01" \
    -H "anthropic-beta: managed-agents-2026-04-01" \
    -H "content-type: application/json" \
    -d '{"events": [{"type": "user.interrupt", "session_thread_id": "'"$THREAD_ID"'"}]}'
  ```

  ```bash CLI
  ant beta:sessions:events send \
    --session-id "$SESSION_ID" \
    --event "{type: user.interrupt, session_thread_id: $THREAD_ID}"
  ```

  ```python Python
  client.beta.sessions.events.send(
      session.id,
      events=[{"type": "user.interrupt", "session_thread_id": thread.id}],
  )
  ```

  ```typescript TypeScript
  await client.beta.sessions.events.send(session.id, {
    events: [{ type: "user.interrupt", session_thread_id: thread.id }]
  });
  ```

  ```csharp C#
  await client.Beta.Sessions.Events.Send(session.ID, new()
  {
      Events =
      [
          new BetaManagedAgentsUserInterruptEventParams
          {
              Type = BetaManagedAgentsUserInterruptEventParamsType.UserInterrupt,
              SessionThreadID = thread.ID,
          },
      ],
  });
  ```

  ```go Go
  if _, err := client.Beta.Sessions.Events.Send(ctx, session.ID, anthropic.BetaSessionEventSendParams{
  	Events: []anthropic.BetaManagedAgentsEventParamsUnion{{
  		OfUserInterrupt: &anthropic.BetaManagedAgentsUserInterruptEventParams{
  			Type:            anthropic.BetaManagedAgentsUserInterruptEventParamsTypeUserInterrupt,
  			SessionThreadID: anthropic.String(thread.ID),
  		},
  	}},
  }); err != nil {
  	panic(err)
  }
  ```

  ```java Java
  client.beta().sessions().events().send(
      session.id(),
      EventSendParams.builder()
          .addEvent(BetaManagedAgentsUserInterruptEventParams.builder()
              .type(BetaManagedAgentsUserInterruptEventParams.Type.USER_INTERRUPT)
              .sessionThreadId(thread.id())
              .build())
          .build());
  ```

  ```php PHP
  $client->beta->sessions->events->send(
      $session->id,
      events: [
          ['type' => 'user.interrupt', 'session_thread_id' => $thread->id],
      ],
  );
  ```

  ```ruby Ruby
  client.beta.sessions.events.send_(
    session.id,
    events: [{type: "user.interrupt", session_thread_id: thread.id}]
  )
  ```
</CodeGroup>

Sur le fil d'un sous-agent bloqué sur `requires_action`, l'interruption ferme chaque appel d'outil en attente avec un résultat d'outil en erreur (« Tool execution was interrupted before completion. Please retry. ») et réémet directement `session.thread_status_idle` avec `stop_reason: end_turn` ; le modèle n'est pas échantillonné. Sur un fil enfant inactif avec `end_turn` ou `budget_reached`, l'interruption n'a aucun effet. Une interruption qui désigne un fil terminé renvoie une erreur 400. Un enfant interrompu n'envoie pas à l'agent du fil principal le rapport qu'il envoie à la fin d'un tour. Tant que cet agent attend l'enfant, il ne commence pas un autre tour avant que quelque chose d'autre ne lui parvienne, comme un `user.message` ou le rapport d'un autre fil.

## Archiver un fil de session

Vous pouvez éventuellement archiver un fil de session lorsqu'il a terminé son travail. L'archivage d'un fil libère sa place dans la limite de 25 fils enfants. Le serveur archive lui-même les fils d'une [exécution de workflow](https://platform.claude.com/docs/fr/managed-agents/workflow-runs). Vous n'avez pas besoin de les archiver, et vous ne pouvez pas le faire tant que l'exécution est ouverte.

<CodeGroup>
  ```bash cURL
  curl -fsS -X POST "https://api.anthropic.com/v1/sessions/$SESSION_ID/threads/$THREAD_ID/archive" \
    -H "x-api-key: $ANTHROPIC_API_KEY" \
    -H "anthropic-version: 2023-06-01" \
    -H "anthropic-beta: managed-agents-2026-04-01"
  ```

  ```bash CLI
  ant beta:sessions:threads archive \
    --session-id "$SESSION_ID" \
    --thread-id "$THREAD_ID"
  ```

  ```python Python
  archived = client.beta.sessions.threads.archive(thread.id, session_id=session.id)
  print(archived.status, archived.archived_at)
  ```

  ```typescript TypeScript
  const archived = await client.beta.sessions.threads.archive(thread.id, {
    session_id: session.id
  });
  console.log(archived.status, archived.archived_at);
  ```

  ```csharp C#
  var archived = await client.Beta.Sessions.Threads.Archive(thread.ID, new() { SessionID = session.ID });
  Console.WriteLine($"{archived.Status} {archived.ArchivedAt}");
  ```

  ```go Go
  archived, err := client.Beta.Sessions.Threads.Archive(ctx, thread.ID, anthropic.BetaSessionThreadArchiveParams{
  	SessionID: session.ID,
  })
  if err != nil {
  	panic(err)
  }
  fmt.Println(archived.Status, archived.ArchivedAt)
  ```

  ```java Java
  var archived = client.beta().sessions().threads().archive(
      thread.id(),
      ThreadArchiveParams.builder()
          .sessionId(session.id())
          .build());
  IO.println(archived.status() + " " + archived.archivedAt().orElseThrow());
  ```

  ```php PHP
  $archived = $client->beta->sessions->threads->archive($thread->id, sessionID: $session->id);
  echo "{$archived->status} {$archived->archivedAt->format(DATE_ATOM)}\n";
  ```

  ```ruby Ruby
  archived = client.beta.sessions.threads.archive(thread.id, session_id: session.id)
  puts "#{archived.status} #{archived.archived_at}"
  ```
</CodeGroup>

L'archivage ne réussit que si le fil est `idle`. Un fil en attente sur `requires_action` est considéré comme inactif et peut être archivé directement ; seul un fil en cours d'exécution doit d'abord être interrompu :

<CodeGroup>
  ```bash cURL
  # Interrompre le thread, puis l'archiver
  curl -fsS "https://api.anthropic.com/v1/sessions/$SESSION_ID/events?beta=true" \
    -H "x-api-key: $ANTHROPIC_API_KEY" \
    -H "anthropic-version: 2023-06-01" \
    -H "anthropic-beta: managed-agents-2026-04-01" \
    -H "content-type: application/json" \
    -d '{"events": [{"type": "user.interrupt", "session_thread_id": "'"$THREAD_ID"'"}]}'

  curl -fsS -X POST "https://api.anthropic.com/v1/sessions/$SESSION_ID/threads/$THREAD_ID/archive" \
    -H "x-api-key: $ANTHROPIC_API_KEY" \
    -H "anthropic-version: 2023-06-01" \
    -H "anthropic-beta: managed-agents-2026-04-01"
  ```

  ```bash CLI
  ant beta:sessions:events send \
    --session-id "$SESSION_ID" \
    --event "{type: user.interrupt, session_thread_id: $THREAD_ID}"

  ant beta:sessions:threads archive \
    --session-id "$SESSION_ID" \
    --thread-id "$THREAD_ID"
  ```

  ```python Python
  client.beta.sessions.events.send(
      session.id,
      events=[{"type": "user.interrupt", "session_thread_id": thread.id}],
  )
  archived = client.beta.sessions.threads.archive(thread.id, session_id=session.id)
  print(archived.status, archived.archived_at)
  ```

  ```typescript TypeScript
  await client.beta.sessions.events.send(session.id, {
    events: [{ type: "user.interrupt", session_thread_id: thread.id }]
  });
  const archived = await client.beta.sessions.threads.archive(thread.id, {
    session_id: session.id
  });
  console.log(archived.status, archived.archived_at);
  ```

  ```csharp C#
  await client.Beta.Sessions.Events.Send(session.ID, new()
  {
      Events =
      [
          new BetaManagedAgentsUserInterruptEventParams
          {
              Type = BetaManagedAgentsUserInterruptEventParamsType.UserInterrupt,
              SessionThreadID = thread.ID,
          },
      ],
  });
  archived = await client.Beta.Sessions.Threads.Archive(thread.ID, new() { SessionID = session.ID });
  Console.WriteLine($"{archived.Status} {archived.ArchivedAt}");
  ```

  ```go Go
  if _, err := client.Beta.Sessions.Events.Send(ctx, session.ID, anthropic.BetaSessionEventSendParams{
  	Events: []anthropic.BetaManagedAgentsEventParamsUnion{{
  		OfUserInterrupt: &anthropic.BetaManagedAgentsUserInterruptEventParams{
  			Type:            anthropic.BetaManagedAgentsUserInterruptEventParamsTypeUserInterrupt,
  			SessionThreadID: anthropic.String(thread.ID),
  		},
  	}},
  }); err != nil {
  	panic(err)
  }

  archived, err := client.Beta.Sessions.Threads.Archive(ctx, thread.ID, anthropic.BetaSessionThreadArchiveParams{
  	SessionID: session.ID,
  })
  if err != nil {
  	panic(err)
  }
  fmt.Println(archived.Status, archived.ArchivedAt)
  ```

  ```java Java
  client.beta().sessions().events().send(
      session.id(),
      EventSendParams.builder()
          .addEvent(BetaManagedAgentsUserInterruptEventParams.builder()
              .type(BetaManagedAgentsUserInterruptEventParams.Type.USER_INTERRUPT)
              .sessionThreadId(thread.id())
              .build())
          .build());

  archived = client.beta().sessions().threads().archive(
      thread.id(),
      ThreadArchiveParams.builder()
          .sessionId(session.id())
          .build());
  IO.println(archived.status() + " " + archived.archivedAt().orElseThrow());
  ```

  ```php PHP
  $client->beta->sessions->events->send(
      $session->id,
      events: [['type' => 'user.interrupt', 'session_thread_id' => $thread->id]],
  );
  $archived = $client->beta->sessions->threads->archive($thread->id, sessionID: $session->id);
  echo "{$archived->status} {$archived->archivedAt->format(DATE_ATOM)}\n";
  ```

  ```ruby Ruby
  client.beta.sessions.events.send_(
    session.id,
    events: [{type: "user.interrupt", session_thread_id: thread.id}]
  )
  archived = client.beta.sessions.threads.archive(thread.id, session_id: session.id)
  puts "#{archived.status} #{archived.archived_at}"
  ```
</CodeGroup>

## Événements du fil principal

Ces événements font apparaître l'activité multi-agents sur le fil principal à `/v1/sessions/{session_id}/events/stream`. Les événements liés aux messages sont nommés du point de vue du fil sur le flux duquel ils apparaissent : `agent.thread_message_received` signifie qu'un message est arrivé sur ce fil depuis un autre fil, et `agent.thread_message_sent` signifie que ce fil en a envoyé un. La tâche que l'agent du fil principal délègue, par exemple, arrive sur le flux du fil enfant sous la forme d'un événement `agent.thread_message_received`.

| Type                               | Description                                                                                                                                                                                                                                                                                                                                         |
| ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `session.thread_created`           | Un fil a été créé. Inclut `session_thread_id` (java: `sessionThreadId`; php: `sessionThreadID`; csharp, go: `SessionThreadID`) et `agent_name` (java, php: `agentName`; csharp, go: `AgentName`).                                                                                                                                                   |
| `session.thread_status_running`    | Un fil a commencé une activité.                                                                                                                                                                                                                                                                                                                     |
| `session.thread_status_idle`       | L'agent associé au fil attend une entrée. Inclut un `stop_reason` (java, php: `stopReason`; csharp, go: `StopReason`) indiquant pourquoi l'agent s'est arrêté.                                                                                                                                                                                      |
| `session.thread_status_terminated` | Un fil s'est terminé et n'accepte plus d'entrée, par exemple parce qu'il a été archivé ou a rencontré une erreur irrécupérable. Un fil du conseiller se termine également lorsque sa consultation prend fin.                                                                                                                                        |
| `agent.thread_message_received`    | Sur le fil principal, un sous-agent a envoyé un rapport ou une question à l'agent du fil principal. Inclut `from_session_thread_id` (java: `fromSessionThreadId`; php: `fromSessionThreadID`; csharp, go: `FromSessionThreadID`), `from_agent_name` (java, php: `fromAgentName`; csharp, go: `FromAgentName`) et `content` (csharp, go: `Content`). |
| `agent.thread_message_sent`        | Sur le fil principal, l'agent du fil principal a envoyé à un sous-agent une tâche ou un message de suivi. Inclut `to_session_thread_id` (java: `toSessionThreadId`; php: `toSessionThreadID`; csharp, go: `ToSessionThreadID`), `to_agent_name` (java, php: `toAgentName`; csharp, go: `ToAgentName`) et `content` (csharp, go: `Content`).         |

Les consultations du conseiller émettent ces mêmes événements de fil sous le nom réservé `anthropic.advisor` (en tant que `agent_name` (java, php: `agentName`; csharp, go: `AgentName`) sur les événements de cycle de vie du fil et `from_agent_name` (java, php: `fromAgentName`; csharp, go: `FromAgentName`) sur la remise du conseil) ; consultez [Donner un conseiller à la session](https://platform.claude.com/docs/fr/managed-agents/multiagent-orchestration#give-the-session-an-advisor) pour la séquence.

Les fils d'une [exécution de workflow](https://platform.claude.com/docs/fr/managed-agents/workflow-runs) apparaissent sur le flux principal comme suit :

* **Événements de cycle de vie :** Chaque fil d'exécution envoie `session.thread_created`, avec le `workflow_run_id` (java: `workflowRunId`; php: `workflowRunID`; csharp, go: `WorkflowRunID`) de l'exécution, ainsi que ses événements `session.thread_status_running`, `session.thread_status_idle` et `session.thread_status_terminated`.
* **Événements de message :** Le prompt d'un fil d'exécution, un événement `agent.thread_message_received`, reste sur son propre flux.
* **Événements d'exécution :** Les événements `workflow_run.*` arrivent également sur ce flux ; consultez [Événements d'exécution](https://platform.claude.com/docs/fr/managed-agents/workflow-runs#run-events).
* **Appels d'outils qui vous attendent :** Les appels d'outils d'un fil d'exécution qui nécessitent votre client sont republiés sur ce flux, comme pour tout fil enfant. Consultez [Autorisations d'outils et outils personnalisés](https://platform.claude.com/docs/fr/managed-agents/session-threads#tool-permissions-and-custom-tools).

## Événements des fils de session

Les événements critiques sont relayés vers le fil principal. Cependant, vous pourriez tout de même vouloir examiner le raisonnement et les appels d'outils d'un agent spécifique. Pour ce faire, diffusez en streaming ou listez les événements du fil de session associé.

Chaque fil de session possède son propre flux d'événements à `/v1/sessions/{session_id}/threads/{thread_id}/stream`, et il accepte le même paramètre `event_deltas[]` que le flux au niveau de la session, ce qui vous permet de prévisualiser le texte d'un sous-agent au fur et à mesure que le modèle le génère. Une connexion ne prévisualise que le fil qu'elle lit : les aperçus d'un fil enfant n'apparaissent jamais sur le flux au niveau de la session ; pour suivre un sous-agent en direct, ouvrez donc son propre flux de fil. Consultez [Prévisualiser les événements des fils de session](https://platform.claude.com/docs/fr/managed-agents/event-deltas#preview-session-thread-events) pour l'activation, l'accumulation et la réconciliation des aperçus.

Dans une exécution de workflow, le serveur exécute un workflow : un programme que l'agent du fil principal écrit. Sur chacun des fils de l'exécution, le premier `agent.thread_message_received` est le prompt que le workflow a écrit. Son `from_session_thread_id` (java: `fromSessionThreadId`; php: `fromSessionThreadID`; csharp, go: `FromSessionThreadID`) est l'ID du fil principal, et l'API ne définit pas son `from_agent_name` (java, php: `fromAgentName`; csharp, go: `FromAgentName`). L'API ne garantit pas le texte du prompt ; ne l'analysez donc pas. L'événement `session.thread_status_terminated` du fil, sur le flux du fil principal, vous indique que le fil a terminé. Aucun événement n'enregistre le résultat qu'il a renvoyé au workflow.

Le flux d'un fil ne rejoue pas les événements antérieurs. Juste après `session.thread_created`, la liste des événements d'un fil d'exécution peut être vide, car le serveur écrit le premier événement du fil après celui-ci. Ouvrez donc d'abord le flux du fil, puis listez les événements du fil, et ignorez chaque événement diffusé dont l'`id` (csharp, go: `ID`) a été renvoyé par la liste.

<Tabs>
  <Tab title="Diffuser les événements d'un fil de session">
    <CodeGroup>
      ```bash cURL
      curl -fsSN "https://api.anthropic.com/v1/sessions/$SESSION_ID/threads/$THREAD_ID/stream?beta=true" \
        -H "x-api-key: $ANTHROPIC_API_KEY" \
        -H "anthropic-version: 2023-06-01" \
        -H "anthropic-beta: managed-agents-2026-04-01"
      ```

      ```bash CLI
      ant beta:sessions:threads:events stream \
        --session-id "$SESSION_ID" \
        --thread-id "$THREAD_ID"
      ```

      ```python Python
      with client.beta.sessions.threads.events.stream(
          thread.id,
          session_id=session.id,
      ) as stream:
          for event in stream:
              match event.type:
                  case "agent.message":
                      for block in event.content:
                          if block.type == "text":
                              print(block.text, end="")
                  case "session.thread_status_idle":
                      break
      ```

      ```typescript TypeScript
      const stream = await client.beta.sessions.threads.events.stream(thread.id, {
        session_id: session.id
      });

      loop: for await (const event of stream) {
        switch (event.type) {
          case "agent.message":
            for (const block of event.content) {
              if (block.type === "text") {
                process.stdout.write(block.text);
              }
            }
            break;
          case "session.thread_status_idle":
            break loop;
        }
      }
      ```

      ```csharp C#
      await foreach (var evt in client.Beta.Sessions.Threads.Events.StreamStreaming(thread.ID, new() { SessionID = session.ID }))
      {
          if (evt.Value is BetaManagedAgentsAgentMessageEvent message)
          {
              foreach (var block in message.Content)
              {
                  if (block.Value is BetaManagedAgentsTextBlock textBlock)
                  {
                      Console.Write(textBlock.Text);
                  }
              }
          }
          else if (evt.Value is BetaManagedAgentsSessionThreadStatusIdleEvent)
          {
              break;
          }
      }
      ```

      ```go Go
      	stream := client.Beta.Sessions.Threads.Events.StreamEvents(ctx, thread.ID, anthropic.BetaSessionThreadEventStreamParams{
      		SessionID: session.ID,
      	})
      	defer stream.Close()

      loop:
      	for stream.Next() {
      		event := stream.Current()
      		switch event.Type {
      		case "agent.message":
      			for _, block := range event.AsAgentMessage().Content {
      				if block.Type == "text" {
      					fmt.Print(block.Text)
      				}
      			}
      		case "session.thread_status_idle":
      			break loop
      		}
      	}
      	if err := stream.Err(); err != nil {
      		panic(err)
      	}
      ```

      ```java Java
      try (var streamResponse = client.beta().sessions().threads().events().streamStreaming(
          thread.id(),
          EventStreamParams.builder().sessionId(session.id()).build()
      )) {
          loop:
          for (var event : (Iterable<BetaManagedAgentsStreamSessionThreadEvents>) streamResponse.stream()::iterator) {
              switch (event.type().value()) {
                  case AGENT_MESSAGE -> {
                      for (var block : event.asAgentMessage().content()) {
                          block.text().ifPresent(textBlock -> IO.print(textBlock.text()));
                      }
                  }
                  case SESSION_THREAD_STATUS_IDLE -> {
                      break loop;
                  }
              }
          }
      }
      ```

      ```php PHP
      $stream = $client->beta->sessions->threads->events->streamStream(
          $thread->id,
          sessionID: $session->id,
      );

      foreach ($stream as $event) {
          switch (true) {
              case $event instanceof \Anthropic\Beta\Sessions\Events\ManagedAgentsAgentMessageEvent:
                  foreach ($event->content as $block) {
                      if ($block instanceof \Anthropic\Beta\Sessions\Events\ManagedAgentsTextBlock) {
                          echo $block->text;
                      }
                  }
                  break;
              case $event instanceof \Anthropic\Beta\Sessions\Events\ManagedAgentsSessionThreadStatusIdleEvent:
                  break 2;
          }
      }
      ```

      ```ruby Ruby
      client.beta.sessions.threads.events.stream_events(thread.id, session_id: session.id).each do |event|
        case event
        when Anthropic::Beta::Sessions::BetaManagedAgentsAgentMessageEvent
          event.content.each do |block|
            print block.text if block.is_a?(Anthropic::Beta::Sessions::BetaManagedAgentsTextBlock)
          end
        when Anthropic::Beta::Sessions::BetaManagedAgentsSessionThreadStatusIdleEvent
          break
        end
      end
      ```
    </CodeGroup>
  </Tab>

  <Tab title="Lister les événements d'un fil de session">
    Listez tous les événements passés d'un fil de session pour récupérer un historique complet.

    <CodeGroup>
      ```bash cURL
      curl -fsS "https://api.anthropic.com/v1/sessions/$SESSION_ID/threads/$THREAD_ID/events" \
        -H "x-api-key: $ANTHROPIC_API_KEY" \
        -H "anthropic-version: 2023-06-01" \
        -H "anthropic-beta: managed-agents-2026-04-01"
      ```

      ```bash CLI
      ant beta:sessions:threads:events list \
        --session-id "$SESSION_ID" \
        --thread-id "$THREAD_ID"
      ```

      ```python Python
      for event in client.beta.sessions.threads.events.list(
          thread.id,
          session_id=session.id,
      ):
          print(f"[{event.type}] {event.processed_at}")
      ```

      ```typescript TypeScript
      for await (const event of client.beta.sessions.threads.events.list(thread.id, {
        session_id: session.id
      })) {
        console.log(`[${event.type}] ${event.processed_at}`);
      }
      ```

      ```csharp C#
      var page = await client.Beta.Sessions.Threads.Events.List(thread.ID, new() { SessionID = session.ID });
      await foreach (var evt in page.Paginate())
      {
          Console.WriteLine($"[{evt.Json.GetProperty("type").GetString()}] {evt.ProcessedAt}");
      }
      ```

      ```go Go
      pager := client.Beta.Sessions.Threads.Events.ListAutoPaging(ctx, thread.ID, anthropic.BetaSessionThreadEventListParams{
      	SessionID: session.ID,
      })
      for pager.Next() {
      	event := pager.Current()
      	fmt.Printf("[%s] %s\n", event.Type, event.ProcessedAt)
      }
      if err := pager.Err(); err != nil {
      	panic(err)
      }
      ```

      ```java Java
      for (var event : client.beta().sessions().threads().events().list(
              thread.id(),
              EventListParams.builder().sessionId(session.id()).build()
          ).autoPager()) {
          var type = event._json().orElseThrow() instanceof JsonObject json
              ? json.values().get("type").asStringOrThrow()
              : "unknown";
          var processedAt = event.processedAt().map(OffsetDateTime::toString).orElse("pending");
          IO.println("[" + type + "] " + processedAt);
      }
      ```

      ```php PHP
      foreach (
          $client->beta->sessions->threads->events->list(
              $thread->id,
              sessionID: $session->id,
          )->pagingEachItem() as $event
      ) {
          echo "[{$event->type}] {$event->processedAt->format(DATE_RFC3339)}\n";
      }
      ```

      ```ruby Ruby
      client.beta.sessions.threads.events.list(
        thread.id,
        session_id: session.id
      ).auto_paging_each do |event|
        puts "[#{event.type}] #{event.processed_at}"
      end
      ```
    </CodeGroup>
  </Tab>
</Tabs>

## Autorisations d'outils et outils personnalisés

Si un sous-agent a besoin de quelque chose de la part de votre client, comme l'[autorisation](https://platform.claude.com/docs/fr/managed-agents/events-and-streaming#tool-confirmation) d'exécuter un appel d'outil ou le [résultat d'un outil personnalisé](https://platform.claude.com/docs/fr/managed-agents/events-and-streaming#handling-custom-tool-calls), l'événement est republié sur le **fil principal** avec `session_thread_id` identifiant le fil de session d'origine. Un appel d'outil nécessite votre autorisation sous `always_ask`, ou sous [`auto`](https://platform.claude.com/docs/fr/managed-agents/permission-policies#let-the-server-evaluate-each-call-with-auto) lorsque le serveur ne parvient à aucune décision.

```json
{
  "type": "session.thread_status_idle",
  "id": "sevt_01ABC...",
  "session_thread_id": "sthr_01DEF...",
  "agent_name": "code-reviewer",
  "stop_reason": {
    "type": "requires_action",
    "event_ids": ["sevt_01XYZ..."]
  }
}
```

Publiez `user.tool_confirmation` (avec `tool_use_id` (java: `toolUseId`; php: `toolUseID`; csharp, go: `ToolUseID`)) ou `user.custom_tool_result` (avec `custom_tool_use_id` (java: `customToolUseId`; php: `customToolUseID`; csharp, go: `CustomToolUseID`)) ; le serveur achemine automatiquement la réponse vers le bon fil. La réponse peut apparaître sur le fil principal et sur le fil du sous-agent avec des valeurs d'`id` (csharp, go: `ID`) différentes. Pour faire correspondre les deux copies, comparez `type` (csharp, go: `Type`) et `tool_use_id` (java: `toolUseId`; php: `toolUseID`; csharp, go: `ToolUseID`) (ou `custom_tool_use_id` (java: `customToolUseId`; php: `customToolUseID`; csharp, go: `CustomToolUseID`)), et non `id` (csharp, go: `ID`).

La session ne passe à `idle` que lorsqu'aucun fil n'est `running` ; `session.status_idle` peut donc arriver longtemps après l'appel d'un sous-agent. Vous n'avez pas à l'attendre : envoyez le `user.custom_tool_result` dès que l'événement `agent.custom_tool_use` republié arrive.

Sous `auto`, vos événements `user.message` peuvent amener le serveur à autoriser un appel qu'il refuserait autrement. Rien dans le fil d'un sous-agent ne compte comme votre intention. Votre client n'y publie aucun message, et les messages que l'agent du fil principal envoie au sous-agent ne comptent pas. Lorsque le serveur refuse un appel sous `auto`, rien n'est republié : l'événement et le résultat d'outil en erreur n'apparaissent que sur le [flux du fil](https://platform.claude.com/docs/fr/managed-agents/session-threads#session-thread-events) du sous-agent, et le sous-agent continue de s'exécuter.

L'exemple suivant se place à l'intérieur de la boucle d'événements du [gestionnaire de confirmation d'outils](https://platform.claude.com/docs/fr/managed-agents/events-and-streaming#tool-confirmation). Pour chaque ID dans `stop_reason.event_ids`, il envoie un `user.tool_confirmation` qui autorise l'appel. Le même schéma s'applique à `user.custom_tool_result`, que vous pouvez également envoyer dès que son événement `agent.custom_tool_use` arrive.

<CodeGroup exclude="shell:cURL, shell:CLI">
  <CodeGroupItem language="cURL">
    Ce workflow nécessite plus de logique qu'une simple 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 workflow nécessite plus de logique qu'une simple commande shell. Utilisez le [SDK Claude](https://platform.claude.com/docs/fr/cli-sdks-libraries/overview) pour l'exécuter.
  </CodeGroupItem>

  ```python Python
  for event_id in stop.event_ids:
      client.beta.sessions.events.send(
          session.id,
          events=[
              {
                  "type": "user.tool_confirmation",
                  "tool_use_id": event_id,
                  "result": "allow",
              }
          ],
      )
  ```

  ```typescript TypeScript
  for (const eventId of stop.event_ids) {
    await client.beta.sessions.events.send(session.id, {
      events: [
        {
          type: "user.tool_confirmation",
          tool_use_id: eventId,
          result: "allow"
        }
      ]
    });
  }
  ```

  ```csharp C#
  foreach (var eventId in requiresAction.EventIds)
  {
      await client.Beta.Sessions.Events.Send(session.ID, new()
      {
          Events =
          [
              new BetaManagedAgentsUserToolConfirmationEventParams
              {
                  Type = BetaManagedAgentsUserToolConfirmationEventParamsType.UserToolConfirmation,
                  ToolUseID = eventId,
                  Result = BetaManagedAgentsUserToolConfirmationEventParamsResult.Allow,
              },
          ],
      });
  }
  ```

  ```go Go
  for _, eventID := range stopReason.EventIDs {
  	params := anthropic.BetaManagedAgentsUserToolConfirmationEventParams{
  		Type:      anthropic.BetaManagedAgentsUserToolConfirmationEventParamsTypeUserToolConfirmation,
  		ToolUseID: eventID,
  		Result:    anthropic.BetaManagedAgentsUserToolConfirmationEventParamsResultAllow,
  	}
  	if _, err := client.Beta.Sessions.Events.Send(ctx, session.ID, anthropic.BetaSessionEventSendParams{
  		Events: []anthropic.BetaManagedAgentsEventParamsUnion{{OfUserToolConfirmation: &params}},
  	}); err != nil {
  		panic(err)
  	}
  }
  ```

  ```java Java
  for (var eventId : pendingToolUseIds) {
      client.beta().sessions().events().send(
          session.id(),
          EventSendParams.builder()
              .addEvent(BetaManagedAgentsUserToolConfirmationEventParams.builder()
                  .type(BetaManagedAgentsUserToolConfirmationEventParams.Type.USER_TOOL_CONFIRMATION)
                  .toolUseId(eventId)
                  .result(BetaManagedAgentsUserToolConfirmationEventParams.Result.ALLOW)
                  .build())
              .build()
      );
  }
  ```

  ```php PHP
  foreach ($event->stopReason->eventIDs as $eventId) {
      $client->beta->sessions->events->send($session->id, events: [[
          'type' => 'user.tool_confirmation',
          'tool_use_id' => $eventId,
          'result' => 'allow',
      ]]);
  }
  ```

  ```ruby Ruby
  event_ids.each do |event_id|
    client.beta.sessions.events.send_(session.id, events: [{
      type: "user.tool_confirmation",
      tool_use_id: event_id,
      result: "allow"
    }])
  end
  ```
</CodeGroup>

Le schéma précédent répond aux appels qu'un événement d'inactivité liste. Sur le flux principal, l'événement `session.thread_status_idle` d'un sous-agent peut arriver avant les événements `agent.tool_use` ou `agent.mcp_tool_use` que liste son `stop_reason.event_ids`. Un `user.tool_confirmation` pour un appel dont l'événement n'est pas encore arrivé peut renvoyer une erreur 400. Pour éviter cela, répondez à chaque appel dont l'[`evaluated_permission` (java, php: `evaluatedPermission`; csharp, go: `EvaluatedPermission`)](https://platform.claude.com/docs/fr/managed-agents/permission-policies#see-how-each-call-was-evaluated) vaut `ask` lorsque son propre événement arrive sur le flux principal.
