---
title: Потоки сессии
url: https://platform.claude.com/docs/ru/managed-agents/session-threads
description: Получайте список потоков мультиагентной сессии, прерывайте и архивируйте их, читайте их события и обрабатывайте разрешения инструментов во всех потоках.
featureMetadata:
  topic:
    title: Managed Agents
    url: https://platform.claude.com/docs/en/managed-agents/overview
  status: beta
  betaHeader: managed-agents-2026-04-01
---

В [мультиагентной сессии](https://platform.claude.com/docs/ru/managed-agents/multiagent-orchestration) каждый агент работает в собственном **session thread** (потоке сессии). На этой странице описано, как получать список потоков, прерывать и архивировать их, какие события они отправляют и как работают разрешения инструментов между ними. [«Workflow run» (запуск рабочего процесса)](https://platform.claude.com/docs/ru/managed-agents/workflow-runs) тоже создаёт потоки сессии.

## Основной поток и потоки сессии

**Поток событий уровня сессии** (`/v1/sessions/{session_id}/events/stream`) считается **primary thread** (основным потоком) и содержит сжатое представление всей активности во всех потоках. Вы не видите полную активность [субагентов](https://platform.claude.com/docs/ru/managed-agents/multiagent-orchestration#delegate-to-subagents), но видите начало и конец их работы, а также блокирующие события, такие как запросы разрешений на использование инструментов.

**Потоки сессии** позволяют детально изучить активность конкретного агента.

`status` (csharp, go: `Status`) сессии отражает совокупную активность всех агентов: если хотя бы один поток находится в состоянии `running`, то и общий статус сессии — `running`. Выполняющийся [запуск рабочего процесса](https://platform.claude.com/docs/ru/managed-agents/workflow-runs) тоже может удерживать сессию в состоянии `running`, даже когда ни один из его потоков не работает. Когда ни один поток не работает и какой-либо поток ожидает вашего клиента, сессия находится в состоянии `idle`; см. [Как узнать, что работа выполнена](https://platform.claude.com/docs/ru/managed-agents/workflow-runs#know-when-the-work-is-done).

[Бюджет сессии](https://platform.claude.com/docs/ru/managed-agents/budgets) — это единый общий лимит для всех потоков сессии. При достижении лимита потоки приостанавливаются независимо друг от друга, а стоимость каждого потока рассчитывается по тарифам модели, которая его обслуживает.

<Note>
  В сессии одновременно может быть не более 25 дочерних потоков. Простаивающие потоки учитываются, пока вы их не заархивируете, а основной поток не учитывается. Агент основного потока может вызывать несколько копий одного субагента, создавая несколько потоков, связанных с одним `agent`. Потоки консультаций [советника](https://platform.claude.com/docs/ru/managed-agents/multiagent-orchestration#give-the-session-an-advisor) не подпадают под это ограничение. Как и потоки [запуска рабочего процесса](https://platform.claude.com/docs/ru/managed-agents/workflow-runs).
</Note>

## Получение списка потоков

Получите список всех потоков сессии следующим образом:

<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>

Полный список включает основной поток. Для основного потока `parent_thread_id` (java: `parentThreadId`; php: `parentThreadID`; csharp, go: `ParentThreadID`) равен `null` (python: `None`; ruby: `nil`; java: `Optional.empty()`; go: `""`). Все остальные потоки являются дочерними. `workflow_run_id` (java: `workflowRunId`; php: `workflowRunID`; csharp, go: `WorkflowRunID`) равен `null` (python: `None`; ruby: `nil`; java: `Optional.empty()`; go: `""`), за исключением [потоков запуска](https://platform.claude.com/docs/ru/managed-agents/workflow-runs#a-runs-threads).

Чтобы получить только потоки с определёнными статусами, добавьте в запрос `statuses[]` и повторите его, чтобы указать более одного статуса, например `?statuses[]=running&statuses[]=idle`. Если параметр не указан, возвращаются потоки с любым статусом.

## Прерывание потока сессии

Отправьте `user.interrupt` с `session_thread_id` (java: `sessionThreadId`; php: `sessionThreadID`; csharp, go: `SessionThreadID`), чтобы остановить конкретный поток. Если не указать `session_thread_id` (java: `sessionThreadId`; php: `sessionThreadID`; csharp, go: `SessionThreadID`), будут прерваны все неархивированные потоки сессии, включая основной. В сессии с динамическими рабочими процессами прерывание не завершает ни один запуск, а прерывание, указывающее поток запуска, ничего не останавливает. Прерывание закрывает ожидающие вызовы инструментов других дочерних потоков, но не полагайтесь на него для закрытия вызовов потока запуска. См. [Прерывание сессии с открытыми запусками](https://platform.claude.com/docs/ru/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>

Для потока субагента, заблокированного в состоянии `requires_action`, прерывание закрывает каждый ожидающий вызов инструмента результатом инструмента с ошибкой («Tool execution was interrupted before completion. Please retry.») и напрямую повторно отправляет `session.thread_status_idle` с `stop_reason: end_turn`; модель при этом не вызывается. Для дочернего потока, простаивающего с `end_turn` или `budget_reached`, прерывание ничего не делает. Прерывание, указывающее завершённый поток, возвращает ошибку 400. Прерванный дочерний поток не отправляет агенту основного потока отчёт, который он отправляет по завершении хода. Пока этот агент ожидает дочерний поток, он не начинает новый ход, пока до него не дойдёт что-то другое, например `user.message` или отчёт другого потока.

## Архивирование потока сессии

При желании заархивируйте поток сессии, когда он завершит свою работу. Архивирование потока освобождает его место в рамках ограничения в 25 дочерних потоков. Сервер сам архивирует потоки [запуска рабочего процесса](https://platform.claude.com/docs/ru/managed-agents/workflow-runs). Вам не нужно их архивировать, и вы не можете сделать это, пока запуск открыт.

<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>

Архивирование выполняется успешно, только если поток находится в состоянии `idle`. Поток, остановленный в состоянии `requires_action`, считается простаивающим и может быть заархивирован напрямую; прервать сначала нужно только выполняющийся поток:

<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"'"}]}'

  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>

## События основного потока

Эти события отражают мультиагентную активность в основном потоке по адресу `/v1/sessions/{session_id}/events/stream`. События, связанные с направлением сообщений, названы относительно потока, в котором они появляются: `agent.thread_message_received` означает, что в этот поток пришло сообщение из другого потока, а `agent.thread_message_sent` означает, что этот поток отправил сообщение. Например, задача, которую делегирует агент основного потока, поступает в собственный поток событий дочернего потока как событие `agent.thread_message_received`.

| Тип                                | Описание                                                                                                                                                                                                                                                                                                                             |
| ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `session.thread_created`           | Был создан поток. Включает `session_thread_id` (java: `sessionThreadId`; php: `sessionThreadID`; csharp, go: `SessionThreadID`) и `agent_name` (java, php: `agentName`; csharp, go: `AgentName`).                                                                                                                                    |
| `session.thread_status_running`    | Поток начал активность.                                                                                                                                                                                                                                                                                                              |
| `session.thread_status_idle`       | Агент, связанный с потоком, ожидает ввода. Включает `stop_reason` (java, php: `stopReason`; csharp, go: `StopReason`), указывающий, почему агент остановился.                                                                                                                                                                        |
| `session.thread_status_terminated` | Поток завершён и больше не принимает ввод, например потому, что он был заархивирован или столкнулся с неустранимой ошибкой. Поток советника также завершается по окончании консультации.                                                                                                                                             |
| `agent.thread_message_received`    | В основном потоке: субагент отправил агенту основного потока отчёт или вопрос. Включает `from_session_thread_id` (java: `fromSessionThreadId`; php: `fromSessionThreadID`; csharp, go: `FromSessionThreadID`), `from_agent_name` (java, php: `fromAgentName`; csharp, go: `FromAgentName`) и `content` (csharp, go: `Content`).      |
| `agent.thread_message_sent`        | В основном потоке: агент основного потока отправил субагенту задачу или дополнительное сообщение. Включает `to_session_thread_id` (java: `toSessionThreadId`; php: `toSessionThreadID`; csharp, go: `ToSessionThreadID`), `to_agent_name` (java, php: `toAgentName`; csharp, go: `ToAgentName`) и `content` (csharp, go: `Content`). |

Консультации с советником генерируют те же события потоков под зарезервированным именем `anthropic.advisor` (как `agent_name` (java, php: `agentName`; csharp, go: `AgentName`) в событиях жизненного цикла потока и как `from_agent_name` (java, php: `fromAgentName`; csharp, go: `FromAgentName`) при доставке совета); последовательность описана в разделе [Предоставление сессии советника](https://platform.claude.com/docs/ru/managed-agents/multiagent-orchestration#give-the-session-an-advisor).

Потоки [запуска рабочего процесса](https://platform.claude.com/docs/ru/managed-agents/workflow-runs) отображаются в основном потоке событий следующим образом:

* **События жизненного цикла:** каждый поток запуска отправляет `session.thread_created` с `workflow_run_id` (java: `workflowRunId`; php: `workflowRunID`; csharp, go: `WorkflowRunID`) запуска, а также свои события `session.thread_status_running`, `session.thread_status_idle` и `session.thread_status_terminated`.
* **События сообщений:** подсказка потока запуска, событие `agent.thread_message_received`, остаётся в его собственном потоке событий.
* **События запуска:** события `workflow_run.*` также поступают в этот поток событий; см. [События запуска](https://platform.claude.com/docs/ru/managed-agents/workflow-runs#run-events).
* **Вызовы инструментов, ожидающие вас:** вызовы инструментов потока запуска, которым нужен ваш клиент, дублируются в этот поток событий, как и для любого дочернего потока. См. [Разрешения инструментов и пользовательские инструменты](https://platform.claude.com/docs/ru/managed-agents/session-threads#tool-permissions-and-custom-tools).

## События потока сессии

Критические события передаются в основной поток. Однако вам всё равно может понадобиться изучить рассуждения и вызовы инструментов конкретного агента. Для этого получайте события связанного потока сессии через потоковую передачу или в виде списка.

У каждого потока сессии есть собственный поток событий по адресу `/v1/sessions/{session_id}/threads/{thread_id}/stream`, и он принимает тот же параметр `event_deltas[]`, что и поток событий уровня сессии, поэтому вы можете предварительно просматривать текст субагента по мере его генерации моделью. Соединение показывает предварительный просмотр только того потока, который оно читает: предварительные просмотры дочернего потока никогда не появляются в потоке событий уровня сессии, поэтому, чтобы наблюдать за субагентом в реальном времени, откройте его собственный поток событий. Сведения о включении, накоплении и согласовании предварительных просмотров см. в разделе [Предварительный просмотр событий потоков выполнения сессии](https://platform.claude.com/docs/ru/managed-agents/event-deltas#preview-session-thread-events).

В запуске рабочего процесса сервер выполняет рабочий процесс — программу, которую пишет агент основного потока. В каждом из потоков запуска первое событие `agent.thread_message_received` — это подсказка, которую написал рабочий процесс. Его `from_session_thread_id` (java: `fromSessionThreadId`; php: `fromSessionThreadID`; csharp, go: `FromSessionThreadID`) — это ID основного потока, а API не задаёт его `from_agent_name` (java, php: `fromAgentName`; csharp, go: `FromAgentName`). API не гарантирует текст подсказки, поэтому не разбирайте его. Событие `session.thread_status_terminated` потока в потоке событий основного потока сообщает вам, что поток завершён. Ни одно событие не фиксирует результат, который он вернул рабочему процессу.

Поток событий потока не воспроизводит более ранние события. Сразу после `session.thread_created` список событий потока запуска может быть пустым, поскольку сервер записывает первое событие потока после него. Поэтому сначала откройте поток событий потока, затем получите список событий потока и пропускайте каждое событие из потоковой передачи, `id` (csharp, go: `ID`) которого вернул список.

<Tabs>
  <Tab title="Потоковая передача событий потока сессии">
    <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="Список событий потока сессии">
    Получите список всех прошлых событий потока сессии, чтобы извлечь полную историю.

    <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>

## Разрешения инструментов и пользовательские инструменты

Если субагенту что-то нужно от вашего клиента, например [разрешение](https://platform.claude.com/docs/ru/managed-agents/events-and-streaming#tool-confirmation) на выполнение вызова инструмента или [результат пользовательского инструмента](https://platform.claude.com/docs/ru/managed-agents/events-and-streaming#handling-custom-tool-calls), событие дублируется в **основной поток** с `session_thread_id`, идентифицирующим исходный поток сессии. Вызову инструмента требуется ваше разрешение при `always_ask` или при [`auto`](https://platform.claude.com/docs/ru/managed-agents/permission-policies#let-the-server-evaluate-each-call-with-auto), когда сервер не приходит к решению.

```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..."]
  }
}
```

Отправьте `user.tool_confirmation` (с `tool_use_id` (java: `toolUseId`; php: `toolUseID`; csharp, go: `ToolUseID`)) или `user.custom_tool_result` (с `custom_tool_use_id` (java: `customToolUseId`; php: `customToolUseID`; csharp, go: `CustomToolUseID`)); сервер автоматически направит ответ в нужный поток. Ответ может появиться в основном потоке и в потоке субагента с разными значениями `id` (csharp, go: `ID`). Чтобы сопоставить две копии, сравнивайте `type` (csharp, go: `Type`) и `tool_use_id` (java: `toolUseId`; php: `toolUseID`; csharp, go: `ToolUseID`) (или `custom_tool_use_id` (java: `customToolUseId`; php: `customToolUseID`; csharp, go: `CustomToolUseID`)), а не `id` (csharp, go: `ID`).

Сессия переходит в состояние `idle`, только когда ни один поток не находится в состоянии `running`, поэтому `session.status_idle` может прийти намного позже вызова субагента. Вам не нужно его ждать: отправьте `user.custom_tool_result`, как только придёт продублированное событие `agent.custom_tool_use`.

При `auto` ваши события `user.message` могут привести к тому, что сервер разрешит вызов, который в противном случае отклонил бы. Ничто в потоке субагента не считается вашим намерением. Ваш клиент не отправляет туда сообщений, а сообщения, которые агент основного потока отправляет субагенту, не учитываются. Когда сервер отклоняет вызов при `auto`, ничего не дублируется: событие и результат инструмента с ошибкой появляются только в собственном [потоке событий](https://platform.claude.com/docs/ru/managed-agents/session-threads#session-thread-events) субагента, и субагент продолжает работу.

Следующий пример размещается внутри цикла событий [обработчика подтверждения инструментов](https://platform.claude.com/docs/ru/managed-agents/events-and-streaming#tool-confirmation). Для каждого ID в `stop_reason.event_ids` он отправляет `user.tool_confirmation`, разрешающий вызов. Тот же шаблон применим к `user.custom_tool_result`, который вы также можете отправить, как только придёт соответствующее событие `agent.custom_tool_use`.

<CodeGroup exclude="shell:cURL, shell:CLI">
  <CodeGroupItem language="cURL">
    Этот рабочий процесс требует больше логики, чем одна команда оболочки. Используйте для его выполнения [Claude SDK](https://platform.claude.com/docs/ru/cli-sdks-libraries/overview).
  </CodeGroupItem>

  <CodeGroupItem language="CLI">
    Этот рабочий процесс требует больше логики, чем одна команда оболочки. Используйте для его выполнения [Claude SDK](https://platform.claude.com/docs/ru/cli-sdks-libraries/overview).
  </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>

Предыдущий шаблон отвечает на вызовы, перечисленные в событии простоя. В основном потоке событий событие `session.thread_status_idle` субагента может прийти раньше событий `agent.tool_use` или `agent.mcp_tool_use`, перечисленных в его `stop_reason.event_ids`. `user.tool_confirmation` для вызова, событие которого ещё не пришло, может вернуть ошибку 400. Чтобы избежать этого, отвечайте на каждый вызов, у которого [`evaluated_permission` (java, php: `evaluatedPermission`; csharp, go: `EvaluatedPermission`)](https://platform.claude.com/docs/ru/managed-agents/permission-policies#see-how-each-call-was-evaluated) равно `ask`, когда его собственное событие приходит в основной поток событий.
