---
title: Запуски рабочих процессов
url: https://platform.claude.com/docs/ru/managed-agents/workflow-runs
description: "Отслеживайте запуски рабочих процессов агента: их состояния и события, момент завершения работы, запросы, пока запуск открыт, бюджеты и ограничения."
featureMetadata:
  topic:
    title: Managed Agents
    url: https://platform.claude.com/docs/en/managed-agents/overview
  status: beta
  betaHeader: managed-agents-2026-04-01
---

**Workflow** (рабочий процесс) — это программа, которую агент пишет, чтобы запустить множество агентов и объединить то, что они возвращают. **Workflow run** (запуск рабочего процесса) — это выполнение одного рабочего процесса. **[Dynamic workflows](https://platform.claude.com/docs/ru/managed-agents/multiagent-orchestration#dynamic-workflows)** (динамические рабочие процессы) — это функция, которая позволяет агенту писать рабочие процессы и начинать запуски. Вы включаете или выключаете её с помощью настройки `workflows` (csharp, go: `Workflows`) в блоке `multiagent` (csharp, go: `Multiagent`) агента.

Сервер выполняет рабочий процесс в фоновом режиме. Его агенты работают в «session threads» ([потоках сессии](https://platform.claude.com/docs/ru/managed-agents/session-threads)), которые сервер создаёт по мере того, как они нужны рабочему процессу. Вы отслеживаете запуски в «event stream» ([потоке событий](https://platform.claude.com/docs/ru/managed-agents/events-and-streaming)) сессии. Чтобы остановить запуск, попросите агента остановить его (см. [Прерывание сессии с открытыми запусками](https://platform.claude.com/docs/ru/managed-agents/workflow-runs#interrupt-a-session-with-runs-open)).

## Как работают динамические рабочие процессы

Агент, которого запускает сессия, пишет каждый рабочий процесс под описанную вами работу. Рабочий процесс — это программа: она запускает других агентов, собирает то, что возвращает каждый из них, и объединяет результаты. Так агент может взяться за задачу, которая слишком велика для одного разговора, например за проверку сотен документов. Во время запуска агент может продолжать работу или завершить свой ход, а также может проверять состояние запуска.

<Frame>
  ![Уровни workflow run (запуска рабочего процесса): сам запуск, две его phases (фазы) и agent threads (потоки агентов) в каждой из них. Результаты передаются из одной фазы в следующую.](https://platform.claude.com/docs/ru/images/workflow-anatomy.svg)
</Frame>

На схеме показан один пример. У каждого рабочего процесса, который пишет агент, свои фазы и агенты. Запуск состоит из следующих уровней:

* **Запуск рабочего процесса:** Сервер выполняет рабочий процесс в фоновом режиме как один запуск рабочего процесса. В сессии одновременно может быть открыто несколько запусков.
* **Фазы:** Рабочий процесс может разделить свою работу на «phases» (фазы). Фаза — это именованный этап запуска, например «Прочитать договоры». Вы отслеживаете ход запуска по событиям его фаз.
* **Потоки агентов:** В фазе программа запускает агентов. Каждый агент работает в собственном [потоке сессии](https://platform.claude.com/docs/ru/managed-agents/session-threads) по подсказке, которую написала программа. Агент в запуске может быть встроенным агентом («inline agent»), которого программа определяет сама, или предопределённым агентом («predefined agent»), которого вы указываете в [списке `predefined_agents` (java, php: `predefinedAgents`; csharp, go: `PredefinedAgents`) настройки `workflows` (csharp, go: `Workflows`)](https://platform.claude.com/docs/ru/managed-agents/multiagent-orchestration#predefined-and-inline-agents). О том, что показывает каждый поток, см. [Потоки запуска](https://platform.claude.com/docs/ru/managed-agents/workflow-runs#a-runs-threads).

Программа может делать следующее:

* **Запускать агентов одновременно:** Программа может запускать множество агентов одновременно, что называется «fanning out» (веерный запуск). На схеме три агента читают договоры в первой фазе.
* **Передавать результаты от одного агента другому:** Каждый агент возвращает свой результат программе. Программа может передать этот результат другому агенту. На схеме агент во второй фазе работает с тем, что вернули первые три. Агенты запуска также работают с одними и теми же файлами в песочнице сессии.
* **Самостоятельно делать следующий шаг:** Результат агента поступает программе, а не агенту, которого запускает сессия. Программа определяет, какие агенты запускаются следующими, и пишет для них подсказки.
* **Повторять и выбирать:** Внутри фазы программа может повторять работу и выбирать следующий шаг на основе того, что вернул агент. Например, она может отправлять черновик на доработку, пока он не пройдёт проверку или не будет исчерпано заданное число раундов. На схеме программа может повторить шаг внутри второй фазы.
* **Обрабатывать сбой агента:** Когда один из её агентов завершается сбоем, сервер обычно запускает этого агента ещё раз в новом потоке, пока остальные агенты запуска продолжают работать. Если он снова завершится сбоем, программа может обработать сбой или позволить ему завершить запуск. Если программа обрабатывает сбой, запуск всё равно может завершиться с `completed`, поэтому попросите агента, которого запускает сессия, сообщать о том, что завершилось сбоем.

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

Вы можете направлять то, как запуск выполняет работу, например как он разделяет работу и что делает при сбое агента. См. [Укажите агенту, когда использовать запуск](https://platform.claude.com/docs/ru/managed-agents/multiagent-orchestration#tell-the-agent-when-to-use-a-run).

Перед началом запуска нет шага утверждения: агент начинает запуск в том же ходе, в котором пишет рабочий процесс. Чтобы сначала проверить план, попросите агента описать запуск и дождаться вашего одобрения. При начале запуска `workflow_run.created` показывает его имя, описание и фазы. Если начался запуск, который вы не одобряли, попросите агента остановить его (см. [Прерывание сессии с открытыми запусками](https://platform.claude.com/docs/ru/managed-agents/workflow-runs#interrupt-a-session-with-runs-open)).

## Как запуск переходит между состояниями

<Frame>
  ![Состояния workflow run (запуска рабочего процесса): running (выполняется), idle (простаивает) и ended (завершён), а также то, что переводит запуск из одного состояния в другое.](https://platform.claude.com/docs/ru/images/workflow-run-states.svg)
</Frame>

Запуск начинается в состоянии running (выполняется) или idle (простаивает). Например, достижение бюджета приостанавливает выполняющийся запуск, и он переходит в состояние idle; повышение или удаление бюджета затем снова возобновляет его выполнение, если только запуск не приостановило ещё и прерывание. Выполняющийся запуск завершается, когда его рабочий процесс заканчивается, агент останавливает его, он завершается сбоем, истекает его время жизни или сессия архивируется. Простаивающий запуск тоже может завершиться, например когда агент останавливает его или сессия архивируется.

Запуск **открыт** с момента события `workflow_run.created` до события `workflow_run.status_ended`, независимо от того, выполняется он или простаивает. Запуск простаивает, пока он приостановлен, например при достижении бюджета сессии. У запуска есть «lifetime» ([время жизни](https://platform.claude.com/docs/ru/managed-agents/workflow-runs#limits)), которое агент может задать при начале запуска. Время, которое запуск проводит в ожидании вашего клиента, засчитывается в это время жизни. Приостановка не останавливает отсчёт времени жизни запуска, поэтому запуск, который остаётся приостановленным, может завершиться с `timeout_error`. Следующие события сообщают о начале запуска, его фазах и его завершении. Приостановка при достижении бюджета тоже отправляет событие. Приостановка после прерывания может не отправить ни одного. Каждое событие `workflow_run.*` включает `workflow_run_id` (java: `workflowRunId`; php: `workflowRunID`; csharp, go: `WorkflowRunID`), которое равно `null` (python: `None`; ruby: `nil`; java: `Optional.empty()`; go: `""`) только в `workflow_run.error`, когда запуск не был создан.

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

События запуска поступают в поток событий сессии, который является потоком событий основного потока, и список событий сессии тоже их возвращает. События запуска не вызывают [вебхуки](https://platform.claude.com/docs/ru/managed-agents/webhooks). События статуса от потоков запуска поступают в тот же поток событий. Каждое из них указывает свой поток в `session_thread_id` (java: `sessionThreadId`; php: `sessionThreadID`; csharp, go: `SessionThreadID`), а потоки запуска — это те, чьё событие `session.thread_created` содержало `workflow_run_id` (java: `workflowRunId`; php: `workflowRunID`; csharp, go: `WorkflowRunID`) запуска.

| Событие                                                  | Когда поступает                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  | Что делать                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| -------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `workflow_run.created`                                   | Агент начал запуск. Включает `workflow_run_id` (java: `workflowRunId`; php: `workflowRunID`; csharp, go: `WorkflowRunID`) (`wrun_…`), `name` (csharp, go: `Name`) и `description` (csharp, go: `Description`) запуска, а также `phases` (csharp, go: `Phases`) — фазы, которые объявляет рабочий процесс, каждая с `id` (csharp, go: `ID`), `name` (csharp, go: `Name`) и `description` (csharp, go: `Description`). `description` (csharp, go: `Description`) равно `null` (python: `None`; ruby: `nil`; java: `Optional.empty()`; go: `""`), если рабочий процесс его не задаёт. `phases` (csharp, go: `Phases`) присутствует всегда и может быть пустым. `name` (csharp, go: `Name`) и `description` (csharp, go: `Description`) запуска и фаз — это текст, написанный моделью, поэтому они могут повторять слова из вашего запроса. `name` (csharp, go: `Name`) запуска также может быть назначено сервером. | Отслеживайте запуск как открытый. Показывайте его `name` (csharp, go: `Name`) и ход выполнения относительно `phases` (csharp, go: `Phases`).                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| `workflow_run.status_running`                            | Когда запуск начинает выполняться, что может произойти через некоторое время после `created`, и каждый раз, когда он возобновляется после приостановки при достижении бюджета. Возобновление после прерывания может его не отправить. Запуск, который начинается в состоянии idle, может сначала получить `workflow_run.status_idle`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            | Показывайте запуск как выполняющийся.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| `workflow_run.status_idle`                               | Запуск был приостановлен, например при достижении бюджета сессии. Событие не сообщает причину. Приостановка после прерывания может его не отправить.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             | Чтобы продолжить, см. [Стоимость и бюджеты](https://platform.claude.com/docs/ru/managed-agents/workflow-runs#cost-and-budgets) или [Прерывание сессии с открытыми запусками](https://platform.claude.com/docs/ru/managed-agents/workflow-runs#interrupt-a-session-with-runs-open).                                                                                                                                                                                                                                                                                                           |
| `workflow_run.phase_started`, `workflow_run.phase_ended` | Рабочий процесс вошёл в фазу или вышел из неё, либо завершение запуска закрыло фазу, которая ещё была открыта. Событие завершения не сообщает, закончилась ли работа фазы. Оба включают `workflow_run_phase_id` (java: `workflowRunPhaseId`; php: `workflowRunPhaseID`; csharp, go: `WorkflowRunPhaseID`). Событие завершения также содержит `phase_started_id` (java: `phaseStartedId`; php: `phaseStartedID`; csharp, go: `PhaseStartedID`) — `id` (csharp, go: `ID`) события начала, которое оно закрывает. Ни одно из них не содержит имени фазы: найдите его по `workflow_run_phase_id` (java: `workflowRunPhaseId`; php: `workflowRunPhaseID`; csharp, go: `WorkflowRunPhaseID`) в `phases` (csharp, go: `Phases`) события `workflow_run.created`.                                                                                                                                                         | Обновляйте ход выполнения. Фазы выполняются по одной, в порядке `phases` (csharp, go: `Phases`), каждая не более одного раза, но API этого не гарантирует. Сопоставляйте завершение фазы с её началом по `phase_started_id` (java: `phaseStartedId`; php: `phaseStartedID`; csharp, go: `PhaseStartedID`). Обрабатывайте случаи, когда открыто более одной фазы, когда фазы нет в `phases` (csharp, go: `Phases`) и когда указанная фаза так и не начинается, даже в запуске, который выполняется до конца. Каждая начавшаяся фаза также завершается до `workflow_run.status_ended` запуска. |
| `workflow_run.status_ended`                              | Запуск завершился. Всегда последнее из событий `workflow_run.*` запуска. Включает `result` (csharp, go: `Result`).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               | Прочитайте `result` (csharp, go: `Result`) (следующая таблица). Затем агент получает ход, чтобы прочитать, как завершился запуск. При достижении бюджета или пока основной поток ожидает вашего клиента этот ход наступает позже. После прерывания этот ход может не наступить: отправьте `user.message` или прочитайте `result` (csharp, go: `Result`) самостоятельно. После архивирования или прекращения он не наступает.                                                                                                                                                                 |
| `workflow_run.error`                                     | Сервер сообщает об ошибке запуска или об отказе в его начале. Запуск, который завершается с `error`, получает это событие с той же ошибкой перед своим `workflow_run.status_ended`. Включает `error` (csharp, go: `Error`): `type` (csharp, go: `Type`) и `message` (csharp, go: `Message`), которое безопасно записывать в журнал. `workflow_run_id` (java: `workflowRunId`; php: `workflowRunID`; csharp, go: `WorkflowRunID`) равно `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: `""`), запуск не начался. В противном случае продолжайте отслеживать запуск до его `workflow_run.status_ended`.                                                                                                                                                                                                                                      |

| `result`                       | Значение                                                                                                                                                                                                                                                                                                                                                                       |
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `{"type": "completed"}`        | Рабочий процесс закончил выполнение. Результат не сообщает, прошла ли работа успешно. Запуск может завершиться с `completed`, даже если работа в его потоках завершилась сбоем или поток не удалось создать. Чтобы найти неудавшуюся работу, прочитайте события каждого из [потоков запуска](https://platform.claude.com/docs/ru/managed-agents/workflow-runs#a-runs-threads). |
| `{"type": "stopped"}`          | Агент остановил запуск, или сессия была архивирована. Событие не сообщает, что именно произошло. Не считайте это единственными возможными причинами.                                                                                                                                                                                                                           |
| `error` с `timeout_error`      | Запуск достиг своего [времени жизни](https://platform.claude.com/docs/ru/managed-agents/workflow-runs#limits), которое может задать агент.                                                                                                                                                                                                                                     |
| `error` с `program_error`      | Рабочий процесс завершился сбоем. Его код завершился сбоем, или он нарушил правило для рабочих процессов, не являющееся ограничением. Либо один из потоков запуска завершился сбоем или не удалось его создать, и рабочий процесс позволил этому завершить запуск.                                                                                                             |
| `error` с `thread_limit_error` | Запуск превысил своё [ограничение на число агентов, которых запускает рабочий процесс](https://platform.claude.com/docs/ru/managed-agents/workflow-runs#limits).                                                                                                                                                                                                               |
| `error` с `unknown_error`      | Сервер не смог продолжить запуск, или запуск превысил одно из других ограничений сервера на рабочие процессы.                                                                                                                                                                                                                                                                  |

Результат с ошибкой выглядит как `{"type": "error", "error": {"type": "timeout_error", "message": "..."}}`, где `message` безопасно записывать в журнал. Считайте нераспознанный `result.type` запуском, который завершился каким-то иным образом, а нераспознанный `error.type` — ошибкой. Когда отказывает что-то, от чего зависит сессия, например модель, сервер MCP, учётные данные или биллинг, поток событий отказавшего потока получает `session.error`. Само по себе это не завершает запуск. Но если это приводит к сбою одного из потоков запуска и рабочий процесс позволяет этому завершить запуск, запуск завершается с `program_error`.

Например, вы спрашиваете [агента проверки договоров](https://platform.claude.com/docs/ru/managed-agents/multiagent-orchestration#turn-on-dynamic-workflows), в каких из 300 договоров есть положение о смене контроля, и агент начинает запуск:

1. `workflow_run.created` называет запуск «Find change-of-control clauses» и перечисляет фазы «Read the contracts» и «Reconcile the findings» в `phases` (csharp, go: `Phases`). Затем следует `workflow_run.status_running`.
2. События фаз отмечают каждую фазу, и каждый поток, который создаёт запуск, отправляет `session.thread_created` с `workflow_run_id` (java: `workflowRunId`; php: `workflowRunID`; csharp, go: `WorkflowRunID`) запуска.
3. Поступает `workflow_run.status_ended` с `result: {"type": "completed"}`.
4. Агент отвечает: «Такое положение есть в 41 из 300 договоров», и поступает `session.status_idle` с `end_turn`.

Первое событие запуска перечисляет его фазы:

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

Каждое событие фазы указывает свою фазу через `workflow_run_phase_id`. Это `id` из `phases`, но API этого не гарантирует:

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

Последнее событие запуска сообщает, как он завершился:

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

## Потоки запуска

Каждый агент в запуске работает в собственном [потоке сессии](https://platform.claude.com/docs/ru/managed-agents/session-threads), который сервер создаёт, когда он нужен рабочему процессу. Вы можете получать список потоков запуска, читать их и получать их события в режиме потоковой передачи, как для любого дочернего потока, а также отвечать на их вызовы инструментов из основного потока событий. Чтобы остановить их, попросите агента остановить запуск (см. [Прерывание сессии с открытыми запусками](https://platform.claude.com/docs/ru/managed-agents/workflow-runs#interrupt-a-session-with-runs-open)).

* **Группировка:** Поток запуска имеет `workflow_run_id` (java: `workflowRunId`; php: `workflowRunID`; csharp, go: `WorkflowRunID`) запуска, как и событие `session.thread_created`, которое о нём сообщает. У других потоков и событий `session.thread_created`, которые о них сообщают, `workflow_run_id` (java: `workflowRunId`; php: `workflowRunID`; csharp, go: `WorkflowRunID`) равно `null` (python: `None`; ruby: `nil`; java: `Optional.empty()`; go: `""`).
* **Агент:** `agent` (csharp, go: `Agent`) показывает агента, которого запускает поток. Для агента, которого вы указали в [списке `predefined_agents` (java, php: `predefinedAgents`; csharp, go: `PredefinedAgents`) настройки `workflows` (csharp, go: `Workflows`)](https://platform.claude.com/docs/ru/managed-agents/multiagent-orchestration#turn-on-dynamic-workflows) в `multiagent` (csharp, go: `Multiagent`), `agent` (csharp, go: `Agent`) содержит `id` (csharp, go: `ID`) и `version` (csharp, go: `Version`) этого агента, как в потоке указанного вами субагента. Для агента, которого определяет рабочий процесс (встроенного агента), `agent` (csharp, go: `Agent`) имеет `type` `inline` и не содержит `id` (csharp, go: `ID`) или `version` (csharp, go: `Version`). Он содержит системную подсказку, которую написал рабочий процесс, а не подсказку агента сессии. Он также содержит имя и описание, которые дал ему рабочий процесс. Сервер назначает имя, если рабочий процесс его не дал. Такой агент использует модель агента сессии — агента, которого запускает сессия. Его инструменты, серверы MCP и навыки являются подмножеством инструментов, серверов MCP и навыков агента сессии. Он получает их все, но API этого не гарантирует. Его инструменты сохраняют свои политики разрешений.
* **Что общего у потоков:** Потоки запуска работают в песочнице сессии, поэтому каждый поток работает с одними и теми же файлами. Это включает файлы хранилища памяти, которое монтирует сессия. Агент, которого определяет рабочий процесс, использует свои серверы MCP с учётными данными, которые сессия для них разрешает. У каждого потока своя история разговора.
* **События:** События `session.thread_created`, `session.thread_status_running`, `session.thread_status_idle` и `session.thread_status_terminated` потока запуска также поступают в основной поток событий (см. [События запуска](https://platform.claude.com/docs/ru/managed-agents/workflow-runs#run-events)). Его события сообщений остаются в его собственном потоке событий. Вебхуки потоков отправляются для него так же, как для любого дочернего потока. О том, что записывает собственный поток событий потока, см. [События потока сессии](https://platform.claude.com/docs/ru/managed-agents/session-threads#session-thread-events).
* **Фазы:** Ни одно событие или поле не сообщает, в какой фазе работает поток, и потоки одного запуска могут иметь одинаковое `agent_name`. Отслеживайте ход запуска по событиям его фаз, а различайте его потоки по `session_thread_id` (java: `sessionThreadId`; php: `sessionThreadID`; csharp, go: `SessionThreadID`).
* **Архивирование:** Сервер архивирует каждый поток не позднее завершения его запуска. Он может архивировать поток раньше, как только поток вернёт свой результат или запуск закончит с ним работу. Если в этот момент поток ещё выполняется или ожидает вашего клиента, сервер сначала останавливает его. Архивированный поток остаётся в списке потоков со статусом `terminated`. Вам не нужно архивировать потоки запуска самостоятельно.
* **Инструменты:** Запуск может создать более одного потока для одной и той же части работы, поэтому сделайте инструменты, которые вызывают ваши агенты, безопасными для повторного вызова.

<Tip>
  Вы можете попросить агента показать рабочий процесс, который он написал для вашего запроса. Дождитесь, пока запуск завершится и сессия перейдёт в состояние `idle`. Затем отправьте `user.message`, в котором попросите агента дословно вывести рабочий процесс, с которым он начал запуск, и не начинать другой запуск.

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

## Как узнать, что работа выполнена

Пока запуск выполняется, ожидайте, что сессия останется в состоянии `running`, даже когда ни один из его потоков не работает. Она переходит в `idle` с `requires_action`, когда ни один поток не работает и какой-либо поток ожидает вашего клиента. Состояние idle само по себе не означает, что работа выполнена. Работа выполнена, когда верны оба условия:

1. Каждый запуск, создание которого вы видели, получил свой `workflow_run.status_ended`.
2. После этого поступает `session.status_idle` с `stop_reason` (java, php: `stopReason`; csharp, go: `StopReason`) `end_turn`, и его не вызвал ваш собственный запрос, например прерывание. После прерывания учитывайте только idle, который наступает после вашего следующего `user.message` или `user.define_outcome`.

* **Приостановленные запуски:** Приостановленный запуск не удерживает сессию в состоянии `running`, поэтому сессия может перейти в idle, пока запуск ещё открыт. Например, при достижении бюджета сессия переходит в idle с `budget_reached`. Работа не выполнена, пока запуск не завершится.
* **Другой запуск:** Агент может начать новый запуск, когда читает результат, поэтому проверяйте снова.
* **Результаты:** Если вы [определили результат](https://platform.claude.com/docs/ru/managed-agents/define-outcomes), никакая оценка не начинается, пока запуск открыт, независимо от того, выполняется он или простаивает. Ход, в котором агент читает результат запуска, может начать оценку.
* **`retries_exhausted`:** Ход агента завершился сбоем из-за ошибки: повторные попытки исчерпаны, или ошибка не допускает повторных попыток, например сбой биллинга. Когда наступает этот idle, запуск может ещё выполняться. Если запуск завершился, а агент ещё не прочитал его результат, сервер начинает новый ход без ввода с вашей стороны. Сессия снова переходит в `running`, поэтому дождитесь следующего idle. Если сессия остаётся в idle, прочитайте `session.error`, который пришёл перед ним, и устраните причину. Затем отправьте `user.message` или прочитайте `result` (csharp, go: `Result`) каждого запуска самостоятельно.

## Отслеживание запуска

Этот пример отслеживает сессию от вашего сообщения до ответа агента. Он открывает поток событий и отправляет сообщение. Затем он делает следующее:

* **Отслеживает каждый запуск** от его `workflow_run.created` до его `workflow_run.status_ended` и выводит каждую фазу при её начале.
* **Отвечает на вызовы пользовательских инструментов** при поступлении каждого `agent.custom_tool_use`, потому что поток запуска может ожидать вашего клиента, пока сессия остаётся в состоянии `running`. Если инструменты вашего агента [запрашивают подтверждение](https://platform.claude.com/docs/ru/managed-agents/events-and-streaming#tool-confirmation), добавьте ветвь, которая отвечает на каждый `agent.tool_use` или `agent.mcp_tool_use`, у которого `evaluated_permission` (java, php: `evaluatedPermission`; csharp, go: `EvaluatedPermission`) равно `ask`. В примере такой ветви нет, потому что ветвь, разрешающая каждый вызов, превратила бы `always_ask` в «всегда разрешать».
* **Останавливается**, когда [работа выполнена](https://platform.claude.com/docs/ru/managed-agents/workflow-runs#know-when-the-work-is-done): ни один запуск не открыт, и сессия переходит в idle с `end_turn`. Он также останавливается, если сессия прекращается. При idle с любой другой причиной остановки, кроме `requires_action`, например `budget_reached`, `retries_exhausted` или `refusal`, он выводит причину и останавливается, поэтому обрабатывайте такие случаи в собственном коде. Он останавливается на `retries_exhausted`, даже когда сервер вот-вот сам начнёт новый ход. Он продолжает ожидание при `requires_action`, а также при `end_turn`, пока запуск открыт.

<CodeGroup exclude="shell">
  <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
  open_runs: dict[str, str] = {}  # workflow_run_id -> run name
  phase_names: dict[tuple[str, str], str] = {}  # (run ID, phase ID) -> phase name

  # Сначала откройте поток, затем отправьте сообщение пользователя
  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":
                  # Отвечайте, когда придёт событие. Поток запуска может ожидать вашего
                  # клиента, пока сеанс остаётся в состоянии выполнения.
                  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:
                      # Сервер отклоняет результат, пришедший слишком поздно, после того как он
                      # заархивировал поток вызова. Продолжайте отслеживать запуск.
                      print(f"  Answer to {event.name} refused: {error.message}")
              case "session.status_idle":
                  # Готово, когда все запуски завершены и агент закончил свой ход
                  if not open_runs and event.stop_reason.type == "end_turn":
                      break
                  # Состояние idle с requires_action ожидает вашего клиента, поэтому продолжайте чтение.
                  # При любой другой причине остановки выведите её и остановитесь.
                  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

  // Сначала откройте поток, затем отправьте сообщение пользователя
  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": {
        // Отвечайте, когда придёт событие. Поток запуска может ожидать ваш
        // клиент, пока сеанс остаётся в состоянии выполнения.
        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) {
          // Сервер отклоняет результат, пришедший слишком поздно, после того как он
          // заархивировал поток вызова. Продолжайте отслеживать запуск.
          if (!(error instanceof Anthropic.BadRequestError)) {
            throw error;
          }
          console.log(`  Answer to ${event.name} refused: ${error.message}`);
        }
        break;
      }
      case "session.status_idle": {
        // Готово, когда все запуски завершились и агент закончил свой ход
        const reason = event.stop_reason.type;
        if (openRuns.size === 0 && reason === "end_turn") {
          break events;
        }
        // Состояние idle с requires_action ожидает ваш клиент, поэтому продолжайте чтение.
        // При любой другой причине остановки выведите её и остановитесь.
        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

  // Сначала откройте поток, затем отправьте сообщение пользователя. Вызов raw-response
  // открывает поток сразу. Обычный вызов ждал бы первого чтения.
  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)
      {
          // Отвечайте, когда придёт событие. Поток выполнения запуска может ждать вашего
          // клиента, пока сеанс остаётся в состоянии running.
          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)
          {
              // Сервер отклоняет результат, пришедший слишком поздно, после того как он
              // заархивировал поток вызова. Продолжайте отслеживать запуск.
              Console.WriteLine($"  Answer to {toolUse.Name} refused: {error.Message}");
          }
      }
      else if (streamEvent.Value is BetaManagedAgentsSessionStatusIdleEvent idle)
      {
          // Готово, когда все запуски завершены и агент закончил свой ход
          var finished = idle.StopReason?.Value is BetaManagedAgentsSessionEndTurn;
          if (openRuns.Count == 0 && finished)
          {
              break;
          }
          // Состояние idle с requires_action ждёт вашего клиента, поэтому продолжайте чтение.
          // При любой другой причине остановки выведите её и остановитесь.
          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

  	// Сначала откройте поток, затем отправьте сообщение пользователя
  	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:
  			// Отвечайте, когда придёт событие. Поток запуска может ожидать вашего
  			// клиента, пока сеанс остаётся в состоянии выполнения.
  			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 {
  				// Сервер отклоняет результат, пришедший слишком поздно, после того как он
  				// заархивировал поток вызова. Продолжайте отслеживать запуск.
  				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:
  			// Готово, когда все запуски завершились и агент закончил свой ход
  			switch event.StopReason.AsAny().(type) {
  			case anthropic.BetaManagedAgentsSessionEndTurn:
  				if len(openRuns) == 0 {
  					break events
  				}
  			// Состояние idle с requires_action ожидает вашего клиента, поэтому продолжайте чтение.
  			// При любой другой причине остановки выведите её и остановитесь.
  			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

  // Сначала откройте поток, затем отправьте сообщение пользователя
  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 -> {
                  // Отвечайте, когда придёт событие. Поток запуска может ожидать вашего
                  // клиента, пока сеанс остаётся в состоянии выполнения.
                  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) {
                      // Сервер отклоняет результат, пришедший слишком поздно, после того как он
                      // заархивировал поток вызова. Продолжайте отслеживать запуск.
                      IO.println("  Answer to " + toolUse.name() + " refused: " + e.getMessage());
                  }
              }
              case SESSION_STATUS_IDLE -> {
                  // Готово, когда все запуски завершились и агент закончил свой ход
                  var stopReason = event.asSessionStatusIdle().stopReason();
                  if (openRuns.isEmpty() && stopReason.isEndTurn()) {
                      break events;
                  }
                  // Состояние idle с requires_action ожидает вашего клиента, поэтому продолжайте чтение.
                  // При любой другой причине остановки выведите её и остановитесь.
                  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]

  // Сначала откройте поток, затем отправьте сообщение пользователя
  $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:
              // Отвечайте, когда придёт событие. Поток запуска может ожидать ваш
              // клиент, пока сеанс остаётся в состоянии выполнения.
              $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) {
                  // Сервер отклоняет результат, пришедший слишком поздно, после того как он
                  // заархивировал поток вызова. Продолжайте отслеживать запуск.
                  echo "  Answer to {$event->name} refused: {$error->getMessage()}", PHP_EOL;
              }
              break;
          case $event instanceof \Anthropic\Beta\Sessions\Events\ManagedAgentsSessionStatusIdleEvent:
              // Готово, когда все запуски завершились и агент закончил свой ход
              $finished = $event->stopReason->type === 'end_turn';
              if (!$openRuns && $finished) {
                  break 2;
              }
              // Состояние idle с requires_action ожидает ваш клиент, поэтому продолжайте чтение.
              // При любой другой причине остановки выведите её и остановитесь.
              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

  # Сначала откройте поток, затем отправьте сообщение пользователя
  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
      # Отвечайте, когда приходит событие. Поток запуска может ожидать ваш
      # клиент, пока сеанс остаётся в состоянии running.
      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
        # Сервер отклоняет результат, пришедший слишком поздно, после архивации
        # потока вызова. Продолжайте отслеживать запуск.
        puts "  Answer to #{event.name} refused: #{error.message}"
      end
    when Anthropic::Beta::Sessions::BetaManagedAgentsSessionStatusIdleEvent
      reason = event.stop_reason.type.to_sym
      # Готово, когда все запуски завершены и агент закончил свой ход
      break if open_runs.empty? && reason == :end_turn
      # Состояние idle с requires_action ожидает ваш клиент, поэтому продолжайте чтение.
      # При любой другой причине остановки выведите её и остановитесь.
      unless %i[end_turn requires_action].include?(reason)
        puts "Session idle: #{reason}"
        break
      end
    when Anthropic::Beta::Sessions::BetaManagedAgentsSessionStatusTerminatedEvent
      break
    end
  end
  ```
</CodeGroup>

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

Отправьте [`user.interrupt`](https://platform.claude.com/docs/ru/managed-agents/events-and-streaming#interrupt-the-agent) без `session_thread_id` (java: `sessionThreadId`; php: `sessionThreadID`; csharp, go: `SessionThreadID`) или с ID основного потока. Это останавливает ход агента. Это не завершает ни одного запуска. Запуски сессии могут приостановиться или продолжить выполнение, и их события могут не показывать, что именно произошло. Время жизни приостановленного запуска продолжает истекать, поэтому запуск может завершиться с `timeout_error`, пока он приостановлен.

* **Ожидающие вызовы инструментов:** После прерывания вызов инструмента потока запуска может всё ещё ожидать вашего клиента. Ответьте на каждый из них. Чтобы отменить вызов, запрашивающий подтверждение, отклоните его. Чтобы отменить вызов пользовательского инструмента, отправьте результат с `is_error` (java, php: `isError`; csharp, go: `IsError`), равным `true`, и текстом в `content` (csharp, go: `Content`), объясняющим причину. Пока сессия находится в состоянии `idle` с `requires_action`, `user.message` возвращает 400, поэтому сначала ответьте на вызовы.
* **Чтобы остановить запуски:** Отправьте `user.message` с просьбой к агенту остановить свои запуски. Остановленный запуск завершается с `result` `{"type": "stopped"}`. Пока сессия находится в состоянии `idle` с `budget_reached`, `user.message` возвращает 400, пока вы не повысите или не удалите бюджет. Повышение или удаление бюджета также возобновляет приостановленные им запуски, если только их не приостановило ещё и прерывание.
* **Чтобы продолжить:** Отправьте `user.message` с просьбой к агенту продолжить свои запуски. После прерывания запуски могут ожидать этого сообщения. Если сессия находится в состоянии `idle` с `budget_reached`, сначала повысьте или удалите бюджет.
* **Результаты запусков:** Запуск, который завершается после прерывания, всё равно отправляет `workflow_run.status_ended`.

## Пока запуск открыт

| Запрос                                                                        | Пока запуск открыт                                                                                                                                                                                                                                                                                                             | Что делать                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| ----------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Архивировать или удалить сессию                                               | Может вернуть 400, пока запуск открыт, независимо от статуса сессии. `error.details.error_code` ошибки может быть `"workflow_run_open"`. Также может завершиться успешно.                                                                                                                                                      | Попросите агента остановить свои запуски или дождитесь завершения каждого запуска. Приостановленный запуск завершается сам, только когда истекает его время жизни. Затем отправьте запрос, когда сессия будет в состоянии `idle`. Успешное архивирование завершает каждый открытый запуск с `{"type": "stopped"}`. После архивирования `workflow_run.status_ended` запуска и `workflow_run.phase_ended` фазы, которая ещё была открыта, не поступают в поток событий. Получите список событий сессии, чтобы прочитать их. После успешного удаления ни одно событие `workflow_run` не сообщает о завершении запусков сессии.                                                                                                                                                                                                                                                                                                                                              |
| Архивировать один из потоков запуска                                          | Возвращает 400 с `error.details.error_code: "workflow_run_open"`, пока запуск открыт, выполняется он или простаивает, если только сервер уже не архивировал поток.                                                                                                                                                             | Ничего. Сервер сам архивирует потоки запуска.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| Обновить `agent` (csharp, go: `Agent`) сессии                                 | Возвращает 400 с `error.details.error_code: "workflow_run_open"`, пока открыт любой запуск, даже приостановленный. Обновление базового агента по-прежнему принимается, и сессия сохраняет свою собственную копию. Запрос, который также отправляет другие поля, например `budget` (csharp, go: `Budget`), отклоняется целиком. | Дождитесь, пока для каждого запуска поступит его `workflow_run.status_ended`, или попросите агента остановить его запуски.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| Ответить на вызов инструмента или подтверждение инструмента из потока запуска | Разрешено. Событие поступает в основной поток событий, и его `session_thread_id` (java: `sessionThreadId`; php: `sessionThreadID`; csharp, go: `SessionThreadID`) указывает поток.                                                                                                                                             | Отвечайте сразу при поступлении события, передавая `id` (csharp, go: `ID`) события как `tool_use_id` (java: `toolUseId`; php: `toolUseID`; csharp, go: `ToolUseID`) или `custom_tool_use_id` (java: `customToolUseId`; php: `customToolUseID`; csharp, go: `CustomToolUseID`). Не ждите `session.status_idle`: сессия может оставаться в состоянии `running`, пока работают другие потоки запуска. После того как сервер архивировал поток, результат инструмента для одного из его вызовов не имеет эффекта и может вернуть 400. Когда результат инструмента возвращает 400, найдите поток вызова в списке потоков. Если его статус `terminated`, результат пришёл слишком поздно, поэтому отбросьте его. Отправляйте каждый результат инструмента в отдельном запросе, потому что сервер отклоняет весь запрос, если отклоняет одно из его событий. Подтверждение инструмента, пришедшее слишком поздно, возвращает 200, что не означает, что инструмент был выполнен. |

### Восстановление состояния запусков после переподключения

Восстанавливайте состояние каждого запуска по событиям сессии. Поток событий не воспроизводит то, что вы пропустили: новое соединение доставляет только события, сгенерированные после его открытия. Поэтому получите список событий с фильтром `types` (curl: `types[]`; cli: `--type`; csharp, go: `Types`), указав по одной записи для каждого типа события, как в разделе [Получение списка прошлых событий](https://platform.claude.com/docs/ru/managed-agents/events-and-streaming#list-past-events). Передавайте `next_page` (ruby: `next_page_`; java, php: `nextPage`; csharp, go: `NextPage`) из каждого ответа как `page` (cli: `--page`; csharp, go: `Page`), пока `next_page` (ruby: `next_page_`; java, php: `nextPage`; csharp, go: `NextPage`) не станет `null` (python: `None`; ruby: `nil`; java: `Optional.empty()`; go: `""`) или не будет отсутствовать. События `workflow_run.created`, `workflow_run.status_running`, `workflow_run.status_idle` и `workflow_run.status_ended` дают состояние каждого запуска, за исключением того, что запуск, приостановленный после прерывания, может по-прежнему отображаться как выполняющийся. События `workflow_run.phase_started` и `workflow_run.phase_ended` позволяют восстановить ход выполнения. Запуск, для которого ещё нет ни одного события статуса, ещё не начал выполняться. Конечной точки для получения списка запусков нет.

## Стоимость и бюджеты

Запросы к модели, которые делает запуск, засчитываются в [бюджет сессии](https://platform.claude.com/docs/ru/managed-agents/budgets). У запуска нет собственной цены. Токены, которые используют его агенты, оплачиваются так же, как другие токены сессии, по тарифам каждой модели. Обо всех расходах сессии см. [Цены на Claude Managed Agents](https://platform.claude.com/docs/ru/about-claude/pricing#claude-managed-agents-pricing).

* **Использование одного запуска:** Получите список потоков сессии и сложите количество токенов в `usage` (csharp, go: `Usage`) потоков с `workflow_run_id` (java: `workflowRunId`; php: `workflowRunID`; csharp, go: `WorkflowRunID`) этого запуска. Список включает архивированные потоки со статусом `terminated`, поэтому потоки завершённого запуска тоже учитываются. Передавайте `next_page` (ruby: `next_page_`; java, php: `nextPage`; csharp, go: `NextPage`) из каждого ответа как `page` (cli: `--page`; csharp, go: `Page`), пока `next_page` (ruby: `next_page_`; java, php: `nextPage`; csharp, go: `NextPage`) не станет `null` (python: `None`; ruby: `nil`; java: `Optional.empty()`; go: `""`) или не будет отсутствовать, и пропускайте потоки без `usage` (csharp, go: `Usage`). Если вместо этого сложить `list_cost` (java, php: `listCost`; csharp, go: `ListCost`) потоков, в итог не войдёт время выполнения сессии, а каждое значение округляется отдельно.
* **При достижении бюджета:** Все открытые запуски приостанавливаются, и сессия сообщает `idle` с `budget_reached` или `requires_action`, если также ожидает вызов инструмента. Каждый поток завершает уже начатый запрос к модели, поэтому запуск может превысить бюджет на один запрос для каждого работающего потока. Повышение или удаление бюджета возобновляет приостановленные им запуски, если только их не приостановило ещё и прерывание. Если использование сессии включает модель без прейскурантной цены, возобновляет их только удаление бюджета; см. [Модели без прейскурантной цены](https://platform.claude.com/docs/ru/managed-agents/budgets#models-without-a-list-price). Время жизни приостановленного запуска продолжает истекать, поэтому запуск может завершиться с `timeout_error`, пока он приостановлен.

## Ограничения

| Ограничение                                                          | Значение                                                   | При достижении ограничения                                                                                                                                                                                                                                   |
| -------------------------------------------------------------------- | ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Потоки, одновременно работающие в одном запуске                      | 64                                                         | Запуск не создаёт новых, пока один из них не завершится. API не гарантирует это число.                                                                                                                                                                       |
| Агенты, которых рабочий процесс запускает за всё время жизни запуска | 1000                                                       | Когда рабочий процесс запрашивает больше, сервер не запускает ещё одного агента, и запуск завершается с `thread_limit_error`. Сервер может повторно запустить агента, завершившегося сбоем, в новом потоке, поэтому у запуска может быть более 1000 потоков. |
| Время жизни запуска                                                  | 24 часа по умолчанию или время жизни, которое задаёт агент | Запуск завершается с `timeout_error`. Ни одно событие не сообщает, какое время жизни задал агент.                                                                                                                                                            |
| Запуски, одновременно открытые в сессии                              | 10 по умолчанию                                            | Сервер отказывается начинать ещё один запуск. Вызов инструмента агента получает ошибку, а вы получаете `workflow_run.error`, у которого `error.type` равен `max_workflow_runs_error`. Простаивающие запуски засчитываются в это ограничение.                 |

Сервер сокращает `name` (csharp, go: `Name`) запуска или фазы до 64 символов, а `description` (csharp, go: `Description`) — до 256. У сервера есть и другие ограничения для рабочих процессов, а также правила для них, которые здесь не перечислены. То, что вы увидите, зависит от того, когда сервер обнаружит проблему:

| Что происходит                                                                                                           | Что вы видите                                                                             |
| ------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------- |
| Рабочий процесс превышает одно из других ограничений, когда агент начинает запуск                                        | Начало запуска отклоняется. Вы получаете `workflow_run.error`, и запуск не создаётся.     |
| Запуск позже превышает одно из других ограничений                                                                        | Вы получаете `workflow_run.error`, после чего запуск может завершиться с `unknown_error`. |
| Сервер после начала обнаруживает, что рабочий процесс нарушает правило для рабочих процессов, не являющееся ограничением | Вы получаете `workflow_run.error`, после чего запуск может завершиться с `program_error`. |

Сессия может начать любое количество запусков за время своей жизни.

* **Начало запусков:** Запуски начинает только агент в основном потоке сессии. Агент, работающий в потоке запуска, не может начать собственный запуск, поэтому запуски не вкладываются друг в друга.
* **Завершение запусков:** Ни одно отправляемое вами событие не завершает запуск; это может сделать архивирование сессии.
* **Потоки запуска:** Вы не можете [остановить поток по его ID](https://platform.claude.com/docs/ru/managed-agents/session-threads#interrupt-a-session-thread) или архивировать его, пока его запуск открыт.
* **Ограничение потоков:** На потоки запуска не распространяется [ограничение дочерних потоков](https://platform.claude.com/docs/ru/managed-agents/session-threads#primary-thread-and-session-threads) сессии.
* **Видимость:** Вы не видите код рабочего процесса, но можете запросить рабочий процесс у агента, как описано в совете в разделе [Потоки запуска](https://platform.claude.com/docs/ru/managed-agents/workflow-runs#a-runs-threads). Вы также не видите вызовы инструментов, которые агент делает для начала запусков и управления ими, и результат, который каждый поток возвращает рабочему процессу.

### Ограничения скорости

Работа запуска засчитывается в ограничения скорости, которые уже действуют для вашей организации.

| Что                                                                                | Засчитывается в                                                                                                                                                              | Что делать                                                                                                                                                                |
| ---------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Запросы вашего клиента на получение сессии, её потоков и их событий или их списков | Ограничение на чтение для [конечных точек Managed Agents](https://platform.claude.com/docs/ru/managed-agents/reference#rate-limits)                                          | Отслеживайте запуск в потоке событий сессии вместо опроса.                                                                                                                |
| Запросы к модели из потоков запуска                                                | Ваши [ограничения скорости Messages API](https://platform.claude.com/docs/ru/api/rate-limits) для модели, которую использует каждый поток, вместе с остальным вашим трафиком | Оставляйте в этих ограничениях запас для запуска или [запросите более высокие ограничения](https://platform.claude.com/docs/ru/api/rate-limits#requesting-higher-limits). |

Когда запрос к модели из одного из потоков запуска ограничивается по скорости или модель перегружена, собственный поток событий этого потока может получить `session.error` типа `model_rate_limited_error` или `model_overloaded_error`:

* Если его `retry_status.type` равен `retrying`, сервер повторяет запрос, и поток продолжает работать.
* Если он равен `exhausted`, поток завершился сбоем. Если рабочий процесс позволяет этому сбою завершить запуск, запуск завершается с `program_error`, который не указывает причину. Прочитайте события потоков, завершившихся сбоем, чтобы найти её.

Сервер также ограничивает объём работы, которую все сессии вашей организации выполняют за минуту. Поток, который достигает этого ограничения, останавливается с `session.error` в собственном потоке событий, сообщение которого указывает на ограничение скорости. Подождите минуту, прежде чем просить агента продолжить.
