---
title: Ejecuciones de flujos de trabajo
url: https://platform.claude.com/docs/es/managed-agents/workflow-runs
description: "Sigue las ejecuciones de flujos de trabajo de un agente: sus estados y eventos, cuándo termina el trabajo, las solicitudes mientras una ejecución está abierta, los presupuestos y los límites."
featureMetadata:
  topic:
    title: Managed Agents
    url: https://platform.claude.com/docs/en/managed-agents/overview
  status: beta
  betaHeader: managed-agents-2026-04-01
---

Un **workflow** (flujo de trabajo) es un programa que un agente escribe para ejecutar muchos agentes y combinar lo que devuelven. Una **workflow run** (ejecución de flujo de trabajo) es la ejecución de un flujo de trabajo. **[Dynamic workflows](https://platform.claude.com/docs/es/managed-agents/multiagent-orchestration#dynamic-workflows)** (flujos de trabajo dinámicos) es la función que permite a un agente escribir flujos de trabajo e iniciar ejecuciones. La activas o desactivas con la configuración `workflows` (csharp, go: `Workflows`) en el bloque `multiagent` (csharp, go: `Multiagent`) del agente.

El servidor ejecuta un flujo de trabajo en segundo plano. Sus agentes trabajan en [hilos de sesión](https://platform.claude.com/docs/es/managed-agents/session-threads) que el servidor crea a medida que el flujo de trabajo los necesita. Sigues las ejecuciones en el [flujo de eventos](https://platform.claude.com/docs/es/managed-agents/events-and-streaming) de la sesión. Para detener una ejecución, pídele al agente que la detenga (consulta [Interrumpir una sesión con ejecuciones abiertas](https://platform.claude.com/docs/es/managed-agents/workflow-runs#interrupt-a-session-with-runs-open)).

## Cómo funcionan los flujos de trabajo dinámicos

El agente que ejecuta la sesión escribe cada flujo de trabajo para el trabajo que describes. Un flujo de trabajo es un programa: ejecuta otros agentes, recopila lo que cada uno devuelve y combina los resultados. De esa manera, el agente puede asumir una tarea demasiado grande para una sola conversación, como la revisión de cientos de documentos. Durante una ejecución, el agente puede seguir trabajando o terminar su turno, y puede consultar el estado de la ejecución.

<Frame>
  ![Capas de una workflow run (ejecución de flujo de trabajo): la ejecución, sus dos fases y los hilos de agente en cada una. Los resultados pasan de una fase a la siguiente.](https://platform.claude.com/docs/es/images/workflow-anatomy.svg)
</Frame>

El diagrama muestra un ejemplo. Cada flujo de trabajo que escribe el agente tiene sus propias fases y agentes. Una ejecución tiene estas capas:

* **Ejecución de flujo de trabajo:** El servidor ejecuta el flujo de trabajo en segundo plano, como una ejecución de flujo de trabajo. Una sesión puede tener varias ejecuciones abiertas al mismo tiempo.
* **Fases:** Un flujo de trabajo puede dividir su trabajo en fases. Una fase es una etapa con nombre de la ejecución, como "Read the contracts" (leer los contratos). Sigues el progreso de una ejecución mediante sus eventos de fase.
* **Hilos de agente:** En una fase, el programa ejecuta agentes. Cada agente trabaja en su propio [hilo de sesión](https://platform.claude.com/docs/es/managed-agents/session-threads), con un prompt que escribió el programa. Un agente en una ejecución puede ser un agente inline, que el propio programa define, o un agente predefinido, que enumeras en la [lista `predefined_agents` (java, php: `predefinedAgents`; csharp, go: `PredefinedAgents`) de `workflows` (csharp, go: `Workflows`)](https://platform.claude.com/docs/es/managed-agents/multiagent-orchestration#predefined-and-inline-agents). Para saber qué muestra cada hilo, consulta [Los hilos de una ejecución](https://platform.claude.com/docs/es/managed-agents/workflow-runs#a-runs-threads).

El programa puede hacer lo siguiente:

* **Ejecutar agentes al mismo tiempo:** El programa puede ejecutar muchos agentes al mismo tiempo, lo que se llama "fanning out" (distribución en abanico). En el diagrama, tres agentes leen contratos en la primera fase.
* **Pasar resultados de un agente a otro:** Cada agente devuelve su resultado al programa. El programa puede pasar ese resultado a otro agente. En el diagrama, el agente de la segunda fase trabaja con lo que devolvieron los tres primeros. Los agentes de una ejecución también trabajan con los mismos archivos, en el sandbox de la sesión.
* **Dar el siguiente paso por sí mismo:** El resultado de un agente va al programa, no al agente que ejecuta la sesión. El programa determina qué agentes se ejecutan a continuación y escribe sus prompts.
* **Repetir y elegir:** Dentro de una fase, el programa puede repetir trabajo y elegir su siguiente paso según lo que devolvió un agente. Por ejemplo, puede hacer que se revise un borrador hasta que pase una revisión o se agote un número determinado de rondas. En el diagrama, el programa puede repetir un paso dentro de la segunda fase.
* **Manejar un agente que falla:** Cuando uno de sus agentes falla, el servidor normalmente ejecuta ese agente una vez más, en un hilo nuevo, mientras los demás agentes de la ejecución siguen trabajando. Si vuelve a fallar, el programa puede manejar el fallo o dejar que termine la ejecución. Si el programa maneja un fallo, la ejecución aún puede terminar como `completed`, así que indícale al agente que ejecuta la sesión que informe qué falló.

Cuando la ejecución termina, el agente que ejecuta la sesión obtiene un turno para leer lo que hizo la ejecución. Luego puede responderte o iniciar otra ejecución. [Eventos de ejecución](https://platform.claude.com/docs/es/managed-agents/workflow-runs#run-events) enumera los casos en los que ese turno llega más tarde o no llega.

Puedes orientar cómo una ejecución realiza el trabajo, por ejemplo cómo divide el trabajo y qué hace cuando un agente falla. Consulta [Indicarle al agente cuándo usar una ejecución](https://platform.claude.com/docs/es/managed-agents/multiagent-orchestration#tell-the-agent-when-to-use-a-run).

No hay un paso de aprobación antes de que comience una ejecución: el agente inicia una ejecución en el mismo turno en que escribe el flujo de trabajo. Para revisar primero un plan, indícale al agente que describa la ejecución y espere tu visto bueno. Cuando una ejecución comienza, `workflow_run.created` muestra su nombre, descripción y fases. Si comienza una ejecución que no aprobaste, pídele al agente que la detenga (consulta [Interrumpir una sesión con ejecuciones abiertas](https://platform.claude.com/docs/es/managed-agents/workflow-runs#interrupt-a-session-with-runs-open)).

## Cómo pasa una ejecución por sus estados

<Frame>
  ![Estados de una workflow run (ejecución de flujo de trabajo): running (en ejecución), idle (inactiva) y ended (finalizada), y qué hace pasar una ejecución de uno a otro.](https://platform.claude.com/docs/es/images/workflow-run-states.svg)
</Frame>

Una ejecución comienza en ejecución o inactiva. Alcanzar el presupuesto, por ejemplo, pausa una ejecución en curso, lo que la deja inactiva; aumentar o eliminar el presupuesto hace que vuelva a ejecutarse, a menos que una interrupción también la haya pausado. Una ejecución en curso termina cuando su flujo de trabajo finaliza, el agente la detiene, falla, se cumple su tiempo de vida o la sesión se archiva. Una ejecución inactiva también puede terminar, por ejemplo cuando el agente la detiene o la sesión se archiva.

Una ejecución está **abierta** desde su evento `workflow_run.created` hasta su evento `workflow_run.status_ended`, ya sea que esté en curso o inactiva. Una ejecución está inactiva mientras está pausada, por ejemplo al alcanzar el presupuesto de la sesión. Una ejecución tiene un [tiempo de vida](https://platform.claude.com/docs/es/managed-agents/workflow-runs#limits), que el agente puede establecer al iniciar la ejecución. El tiempo que una ejecución pasa esperando a tu cliente cuenta para ese tiempo de vida. Una pausa no impide que transcurra el tiempo de vida de una ejecución, por lo que una ejecución que permanece pausada puede terminar con `timeout_error`. Los siguientes eventos informan el inicio de una ejecución, sus fases y su final. Una pausa al alcanzar el presupuesto también envía uno. Una pausa después de una interrupción podría no enviar ninguno. Cada evento `workflow_run.*` incluye `workflow_run_id` (java: `workflowRunId`; php: `workflowRunID`; csharp, go: `WorkflowRunID`), que es `null` (python: `None`; ruby: `nil`; java: `Optional.empty()`; go: `""`) solo en un `workflow_run.error` cuando no se creó ninguna ejecución.

## Eventos de ejecución

Los eventos de ejecución llegan al flujo de eventos de la sesión, que es el flujo del hilo principal, y listar los eventos de la sesión también los devuelve. Los eventos de ejecución no activan [webhooks](https://platform.claude.com/docs/es/managed-agents/webhooks). Los eventos de estado de los hilos de la ejecución llegan al mismo flujo. Cada uno nombra su hilo en `session_thread_id` (java: `sessionThreadId`; php: `sessionThreadID`; csharp, go: `SessionThreadID`), y los hilos de una ejecución son aquellos cuyo evento `session.thread_created` tenía el `workflow_run_id` (java: `workflowRunId`; php: `workflowRunID`; csharp, go: `WorkflowRunID`) de la ejecución.

| Evento                                                   | Cuándo llega                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    | Qué hacer                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| -------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `workflow_run.created`                                   | El agente inició una ejecución. Incluye `workflow_run_id` (java: `workflowRunId`; php: `workflowRunID`; csharp, go: `WorkflowRunID`) (`wrun_…`), el `name` (csharp, go: `Name`) y la `description` (csharp, go: `Description`) de la ejecución, y `phases` (csharp, go: `Phases`), las fases que declara el flujo de trabajo, cada una con un `id` (csharp, go: `ID`), un `name` (csharp, go: `Name`) y una `description` (csharp, go: `Description`). Una `description` (csharp, go: `Description`) es `null` (python: `None`; ruby: `nil`; java: `Optional.empty()`; go: `""`) cuando el flujo de trabajo no proporciona ninguna. `phases` (csharp, go: `Phases`) siempre está presente y puede estar vacío. El `name` (csharp, go: `Name`) y la `description` (csharp, go: `Description`) de la ejecución y de las fases son texto que escribió el modelo, por lo que pueden repetir palabras de tu solicitud. El `name` (csharp, go: `Name`) de una ejecución también puede ser uno que asignó el servidor. | Registra la ejecución como abierta. Muestra su `name` (csharp, go: `Name`) y el progreso respecto a `phases` (csharp, go: `Phases`).                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `workflow_run.status_running`                            | Cuando la ejecución comienza a ejecutarse, lo que puede ocurrir un tiempo después de `created`, y cada vez que se reanuda después de una pausa al alcanzar el presupuesto. Una reanudación después de una interrupción podría no enviarlo. Una ejecución que comienza inactiva podría recibir primero `workflow_run.status_idle`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               | Muestra la ejecución como en ejecución.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `workflow_run.status_idle`                               | La ejecución se pausó, por ejemplo al alcanzar el presupuesto de la sesión. El evento no indica por qué. Una pausa después de una interrupción podría no enviarlo.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              | Para continuar, consulta [Costo y presupuestos](https://platform.claude.com/docs/es/managed-agents/workflow-runs#cost-and-budgets) o [Interrumpir una sesión con ejecuciones abiertas](https://platform.claude.com/docs/es/managed-agents/workflow-runs#interrupt-a-session-with-runs-open).                                                                                                                                                                                                                                                                                                       |
| `workflow_run.phase_started`, `workflow_run.phase_ended` | El flujo de trabajo entró en una fase o salió de ella, o el final de la ejecución cerró una fase que seguía abierta. El evento de fin no indica si el trabajo de la fase terminó. Ambos incluyen `workflow_run_phase_id` (java: `workflowRunPhaseId`; php: `workflowRunPhaseID`; csharp, go: `WorkflowRunPhaseID`). El de fin también tiene `phase_started_id` (java: `phaseStartedId`; php: `phaseStartedID`; csharp, go: `PhaseStartedID`), el `id` (csharp, go: `ID`) del evento de inicio que cierra. Ninguno tiene el nombre de la fase: búscalo por `workflow_run_phase_id` (java: `workflowRunPhaseId`; php: `workflowRunPhaseID`; csharp, go: `WorkflowRunPhaseID`) en las `phases` (csharp, go: `Phases`) de `workflow_run.created`.                                                                                                                                                                                                                                                                   | Actualiza el progreso. Las fases se ejecutan una a la vez, en el orden de `phases` (csharp, go: `Phases`), cada una como máximo una vez, pero la API no lo garantiza. Empareja el fin de una fase con su inicio mediante `phase_started_id` (java: `phaseStartedId`; php: `phaseStartedID`; csharp, go: `PhaseStartedID`). Maneja más de una fase abierta, una fase que no está en `phases` (csharp, go: `Phases`) y una fase listada que nunca comienza, incluso en una ejecución que se completa. Toda fase que comienza también termina, antes del `workflow_run.status_ended` de la ejecución. |
| `workflow_run.status_ended`                              | La ejecución terminó. Siempre es el último de los eventos `workflow_run.*` de la ejecución. Incluye `result` (csharp, go: `Result`).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            | Lee `result` (csharp, go: `Result`) (tabla siguiente). Luego el agente obtiene un turno para leer cómo terminó la ejecución. Al alcanzar el presupuesto, o mientras el hilo principal espera a tu cliente, ese turno llega más tarde. Después de una interrupción, ese turno podría no llegar: envía un `user.message` o lee `result` (csharp, go: `Result`) tú mismo. Después de un archivado o una terminación, no llega.                                                                                                                                                                        |
| `workflow_run.error`                                     | El servidor informa un error de una ejecución, o un inicio que rechazó. Una ejecución que termina en `error` recibe este evento, con el mismo error, antes de su `workflow_run.status_ended`. Incluye `error` (csharp, go: `Error`): un `type` (csharp, go: `Type`) y un `message` (csharp, go: `Message`) que es seguro registrar. `workflow_run_id` (java: `workflowRunId`; php: `workflowRunID`; csharp, go: `WorkflowRunID`) es `null` (python: `None`; ruby: `nil`; java: `Optional.empty()`; go: `""`) cuando no se creó ninguna ejecución.                                                                                                                                                                                                                                                                                                                                                                                                                                                               | Regístralo y no lo tomes como el final de la ejecución. Si `workflow_run_id` (java: `workflowRunId`; php: `workflowRunID`; csharp, go: `WorkflowRunID`) es `null` (python: `None`; ruby: `nil`; java: `Optional.empty()`; go: `""`), no comenzó ninguna ejecución. De lo contrario, sigue registrando la ejecución hasta su `workflow_run.status_ended`.                                                                                                                                                                                                                                           |

| `result`                         | Significado                                                                                                                                                                                                                                                                                                                                                                                             |
| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `{"type": "completed"}`          | El flujo de trabajo terminó de ejecutarse. El resultado no indica si el trabajo fue exitoso. Una ejecución puede terminar como `completed` aunque haya fallado trabajo en sus hilos, o no se haya podido crear un hilo. Para encontrar el trabajo fallido, lee los eventos de cada uno de los [hilos de la ejecución](https://platform.claude.com/docs/es/managed-agents/workflow-runs#a-runs-threads). |
| `{"type": "stopped"}`            | El agente detuvo la ejecución, o se archivó la sesión. El evento no indica cuál. No trates estas como las únicas causas.                                                                                                                                                                                                                                                                                |
| `error` con `timeout_error`      | La ejecución alcanzó su [tiempo de vida](https://platform.claude.com/docs/es/managed-agents/workflow-runs#limits), que el agente puede establecer.                                                                                                                                                                                                                                                      |
| `error` con `program_error`      | El flujo de trabajo falló. Su código falló, o infringió una regla de los flujos de trabajo, distinta de un límite. O uno de los hilos de la ejecución falló, o no se pudo crear, y el flujo de trabajo dejó que eso terminara la ejecución.                                                                                                                                                             |
| `error` con `thread_limit_error` | La ejecución superó su [límite de agentes que inicia un flujo de trabajo](https://platform.claude.com/docs/es/managed-agents/workflow-runs#limits).                                                                                                                                                                                                                                                     |
| `error` con `unknown_error`      | El servidor no pudo continuar la ejecución, o la ejecución superó uno de los otros límites del servidor sobre los flujos de trabajo.                                                                                                                                                                                                                                                                    |

Un resultado de error tiene el aspecto `{"type": "error", "error": {"type": "timeout_error", "message": "..."}}`, donde es seguro registrar `message`. Trata un `result.type` no reconocido como una ejecución que terminó de alguna otra manera, y un `error.type` no reconocido como un error. Cuando falla algo de lo que depende la sesión, como el modelo, un servidor MCP, las credenciales o la facturación, el flujo del hilo que falla recibe un `session.error`. Eso no termina una ejecución por sí solo. Pero si hace que falle uno de los hilos de la ejecución, y el flujo de trabajo deja que eso termine la ejecución, la ejecución termina con `program_error`.

Por ejemplo, le preguntas al [agente de revisión de contratos](https://platform.claude.com/docs/es/managed-agents/multiagent-orchestration#turn-on-dynamic-workflows) cuáles de 300 contratos tienen una cláusula de cambio de control, y el agente inicia una ejecución:

1. `workflow_run.created` nombra la ejecución "Find change-of-control clauses" y enumera las fases "Read the contracts" y "Reconcile the findings" en `phases` (csharp, go: `Phases`). Luego sigue `workflow_run.status_running`.
2. Los eventos de fase marcan cada fase, y cada hilo que crea la ejecución envía `session.thread_created` con el `workflow_run_id` (java: `workflowRunId`; php: `workflowRunID`; csharp, go: `WorkflowRunID`) de la ejecución.
3. `workflow_run.status_ended` llega con `result: {"type": "completed"}`.
4. El agente responde: "41 de los 300 contratos tienen una", y `session.status_idle` llega con `end_turn`.

El primer evento de la ejecución enumera sus fases:

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

Cada evento de fase nombra su fase mediante `workflow_run_phase_id`. Ese es un `id` en `phases`, pero la API no lo garantiza:

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

El último evento de la ejecución informa cómo terminó:

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

## Los hilos de una ejecución

Cada agente de una ejecución trabaja en su propio [hilo de sesión](https://platform.claude.com/docs/es/managed-agents/session-threads), que el servidor crea a medida que el flujo de trabajo lo necesita. Puedes listar, leer y hacer streaming de los hilos de una ejecución como con cualquier hilo hijo, y responder a sus llamadas a herramientas desde el flujo principal. Para detenerlos, pídele al agente que detenga la ejecución (consulta [Interrumpir una sesión con ejecuciones abiertas](https://platform.claude.com/docs/es/managed-agents/workflow-runs#interrupt-a-session-with-runs-open)).

* **Agrupación:** Un hilo de una ejecución tiene el `workflow_run_id` (java: `workflowRunId`; php: `workflowRunID`; csharp, go: `WorkflowRunID`) de la ejecución, al igual que el evento `session.thread_created` que lo anuncia. Los demás hilos, y los eventos `session.thread_created` que los anuncian, tienen `workflow_run_id` (java: `workflowRunId`; php: `workflowRunID`; csharp, go: `WorkflowRunID`) establecido en `null` (python: `None`; ruby: `nil`; java: `Optional.empty()`; go: `""`).
* **Agente:** `agent` (csharp, go: `Agent`) muestra el agente que ejecuta el hilo. Para un agente que enumeraste en la [lista `predefined_agents` (java, php: `predefinedAgents`; csharp, go: `PredefinedAgents`) de `workflows` (csharp, go: `Workflows`)](https://platform.claude.com/docs/es/managed-agents/multiagent-orchestration#turn-on-dynamic-workflows) en `multiagent` (csharp, go: `Multiagent`), `agent` (csharp, go: `Agent`) tiene el `id` (csharp, go: `ID`) y la `version` (csharp, go: `Version`) de ese agente, como en el hilo de un subagente que enumeraste. Para un agente que define el flujo de trabajo (un agente inline), `agent` (csharp, go: `Agent`) tiene `type` `inline` y no tiene `id` (csharp, go: `ID`) ni `version` (csharp, go: `Version`). Tiene la indicación del sistema que escribió el flujo de trabajo, no la del agente de la sesión. También tiene el nombre y la descripción que le dio el flujo de trabajo. El servidor asigna un nombre si el flujo de trabajo no le dio ninguno. Usa el modelo del agente de la sesión, el agente que ejecuta la sesión. Sus herramientas, servidores MCP y skills son un subconjunto de los del agente de la sesión. Los recibe todos, pero la API no lo garantiza. Sus herramientas conservan sus políticas de permisos.
* **Lo que comparten los hilos:** Los hilos de una ejecución trabajan en el sandbox de la sesión, por lo que todos los hilos trabajan con los mismos archivos. Eso incluye los archivos de un almacén de memoria que monta la sesión. Un agente que define el flujo de trabajo usa sus servidores MCP con las credenciales que la sesión resuelve para ellos. Cada hilo tiene su propio historial de conversación.
* **Eventos:** Los eventos `session.thread_created`, `session.thread_status_running`, `session.thread_status_idle` y `session.thread_status_terminated` de un hilo de ejecución también llegan al flujo principal (consulta [Eventos de ejecución](https://platform.claude.com/docs/es/managed-agents/workflow-runs#run-events)). Sus eventos de mensaje permanecen en su propio flujo. Sus webhooks de hilo se envían como para cualquier hilo hijo. Para saber qué registra el propio flujo del hilo, consulta [Eventos del hilo de sesión](https://platform.claude.com/docs/es/managed-agents/session-threads#session-thread-events).
* **Fases:** Ningún evento ni campo indica en qué fase trabaja un hilo, y los hilos de una misma ejecución pueden tener el mismo `agent_name`. Sigue el progreso de una ejecución mediante sus eventos de fase y distingue sus hilos por `session_thread_id` (java: `sessionThreadId`; php: `sessionThreadID`; csharp, go: `SessionThreadID`).
* **Archivado:** El servidor archiva cada hilo a más tardar al final de su ejecución. Puede archivar uno antes, una vez que el hilo devuelve su resultado o la ejecución termina con él. Si en ese momento el hilo sigue en curso o esperando a tu cliente, el servidor lo detiene primero. Un hilo archivado permanece en la lista de hilos, con estado `terminated`. No necesitas archivar tú mismo los hilos de una ejecución.
* **Herramientas:** Una ejecución puede crear más de un hilo para la misma parte del trabajo, así que haz que las herramientas que llaman tus agentes sean seguras de llamar dos veces.

<Tip>
  Puedes pedirle al agente el flujo de trabajo que escribió para tu solicitud. Espera hasta que la ejecución haya terminado y la sesión esté `idle`. Luego envía un `user.message` que le pida al agente que imprima, palabra por palabra, el flujo de trabajo con el que inició la ejecución, y que no inicie otra ejecución.

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

## Saber cuándo termina el trabajo

Mientras una ejecución está en curso, espera que la sesión permanezca `running`, incluso cuando ninguno de sus hilos está trabajando. Pasa a `idle` con `requires_action` cuando ningún hilo está trabajando y un hilo espera a tu cliente. Un estado inactivo por sí solo no significa que el trabajo haya terminado. El trabajo termina cuando se cumplen ambas condiciones:

1. Cada ejecución que has visto crearse tiene su `workflow_run.status_ended`.
2. Después de eso, llega un `session.status_idle` con `stop_reason` (java, php: `stopReason`; csharp, go: `StopReason`) `end_turn`, y no lo causó una solicitud tuya, como una interrupción. Después de interrumpir, cuenta solo un estado inactivo que llegue después de tu siguiente `user.message` o `user.define_outcome`.

* **Ejecuciones pausadas:** Una ejecución pausada no mantiene la sesión en `running`, por lo que la sesión puede quedar inactiva mientras la ejecución sigue abierta. Al alcanzar el presupuesto, por ejemplo, la sesión queda inactiva con `budget_reached`. El trabajo no termina hasta que termina la ejecución.
* **Otra ejecución:** El agente puede iniciar una nueva ejecución cuando lee un resultado, así que vuelve a comprobarlo.
* **"Outcomes" (resultados):** Si [definiste un resultado](https://platform.claude.com/docs/es/managed-agents/define-outcomes), no comienza ninguna evaluación mientras una ejecución está abierta, ya sea en ejecución o inactiva. El turno en el que el agente lee el resultado de la ejecución puede iniciar una.
* **`retries_exhausted`:** El turno del agente falló por un error: se agotaron los reintentos, o el error no se puede reintentar, como un fallo de facturación. Una ejecución podría seguir en curso cuando llega este estado inactivo. Si una ejecución terminó y el agente aún no ha leído su resultado, el servidor inicia un nuevo turno sin ninguna entrada tuya. La sesión vuelve a `running`, así que espera al siguiente estado inactivo. Si la sesión permanece inactiva, lee el `session.error` que llegó antes y corrige la causa. Luego envía un `user.message`, o lee tú mismo el `result` (csharp, go: `Result`) de cada ejecución.

## Seguir una ejecución

Este ejemplo sigue una sesión desde tu mensaje hasta la respuesta del agente. Abre el flujo y envía el mensaje. Luego hace lo siguiente:

* **Registra cada ejecución** desde su `workflow_run.created` hasta su `workflow_run.status_ended`, e imprime cada fase cuando comienza.
* **Responde a las llamadas a herramientas personalizadas** cuando llega cada `agent.custom_tool_use`, porque un hilo de una ejecución puede esperar a tu cliente mientras la sesión permanece `running`. Si las herramientas de tu agente [piden confirmación](https://platform.claude.com/docs/es/managed-agents/events-and-streaming#tool-confirmation), agrega una rama que responda a cada `agent.tool_use` o `agent.mcp_tool_use` cuyo `evaluated_permission` (java, php: `evaluatedPermission`; csharp, go: `EvaluatedPermission`) sea `ask`. El ejemplo no tiene ninguna, porque una rama que permita todas las llamadas convertiría `always_ask` en permitir siempre.
* **Se detiene** cuando el [trabajo termina](https://platform.claude.com/docs/es/managed-agents/workflow-runs#know-when-the-work-is-done): no hay ninguna ejecución abierta y la sesión queda inactiva con `end_turn`. También se detiene si la sesión termina. En un estado inactivo con cualquier otro motivo de detención excepto `requires_action`, como `budget_reached`, `retries_exhausted` o `refusal`, imprime el motivo y se detiene, así que maneja esos casos en tu propio código. Se detiene en `retries_exhausted` incluso cuando el servidor está a punto de iniciar un nuevo turno por sí mismo. Sigue esperando en `requires_action`, y en `end_turn` mientras una ejecución está abierta.

<CodeGroup exclude="shell">
  <CodeGroupItem language="cURL">
    Este flujo de trabajo necesita más lógica que un solo comando de shell. Usa el [SDK de Claude](https://platform.claude.com/docs/es/cli-sdks-libraries/overview) para ejecutarlo.
  </CodeGroupItem>

  <CodeGroupItem language="CLI">
    Este flujo de trabajo necesita más lógica que un solo comando de shell. Usa el [SDK de Claude](https://platform.claude.com/docs/es/cli-sdks-libraries/overview) para ejecutarlo.
  </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

  # Abre primero el stream y luego envía el mensaje del usuario
  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":
                  # Responde cuando llegue el evento. El hilo de una ejecución puede esperar a tu
                  # cliente mientras la sesión sigue en ejecución.
                  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:
                      # El servidor rechaza un resultado que llega demasiado tarde, después de que
                      # archivó el hilo de la llamada. Sigue la ejecución.
                      print(f"  Answer to {event.name} refused: {error.message}")
              case "session.status_idle":
                  # Termina cuando todas las ejecuciones han finalizado y el agente ha terminado su turno
                  if not open_runs and event.stop_reason.type == "end_turn":
                      break
                  # Un idle con requires_action espera a tu cliente, así que sigue leyendo.
                  # Ante cualquier otro motivo de parada, imprímelo y detente.
                  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

  // Abre primero el stream y luego envía el mensaje del usuario
  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": {
        // Responde cuando llegue el evento. El hilo de una ejecución puede esperar a tu
        // cliente mientras la sesión sigue en ejecución.
        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) {
          // El servidor rechaza un resultado que llega demasiado tarde, después de que
          // archivó el hilo de la llamada. Sigue la ejecución.
          if (!(error instanceof Anthropic.BadRequestError)) {
            throw error;
          }
          console.log(`  Answer to ${event.name} refused: ${error.message}`);
        }
        break;
      }
      case "session.status_idle": {
        // Termina cuando todas las ejecuciones han finalizado y el agente ha terminado su turno
        const reason = event.stop_reason.type;
        if (openRuns.size === 0 && reason === "end_turn") {
          break events;
        }
        // Un idle con requires_action espera a tu cliente, así que sigue leyendo.
        // Ante cualquier otro motivo de parada, imprímelo y detente.
        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

  // Abre el stream primero y luego envía el mensaje del usuario. La llamada raw-response
  // abre el stream de inmediato. La llamada simple esperaría a la primera lectura.
  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)
      {
          // Responde cuando llegue el evento. El hilo de una ejecución puede esperar a tu
          // cliente mientras la sesión sigue en ejecución.
          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)
          {
              // El servidor rechaza un resultado que llega demasiado tarde, después de
              // archivar el hilo de la llamada. Sigue siguiendo la ejecución.
              Console.WriteLine($"  Answer to {toolUse.Name} refused: {error.Message}");
          }
      }
      else if (streamEvent.Value is BetaManagedAgentsSessionStatusIdleEvent idle)
      {
          // Termina cuando todas las ejecuciones han finalizado y el agente ha terminado su turno
          var finished = idle.StopReason?.Value is BetaManagedAgentsSessionEndTurn;
          if (openRuns.Count == 0 && finished)
          {
              break;
          }
          // Un idle con requires_action espera a tu cliente, así que sigue leyendo.
          // Ante cualquier otro motivo de parada, imprímelo y detente.
          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

  	// Abre primero el stream y luego envía el mensaje del usuario
  	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:
  			// Responde cuando llegue el evento. El hilo de una ejecución puede esperar a tu
  			// cliente mientras la sesión sigue en ejecución.
  			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 {
  				// El servidor rechaza un resultado que llega demasiado tarde, después de que
  				// archivó el hilo de la llamada. Sigue la ejecución.
  				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:
  			// Termina cuando todas las ejecuciones han finalizado y el agente ha completado su turno
  			switch event.StopReason.AsAny().(type) {
  			case anthropic.BetaManagedAgentsSessionEndTurn:
  				if len(openRuns) == 0 {
  					break events
  				}
  			// Un idle con requires_action espera a tu cliente, así que sigue leyendo.
  			// Ante cualquier otro motivo de parada, imprímelo y detente.
  			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

  // Abre primero el stream y luego envía el mensaje del usuario
  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 -> {
                  // Responde cuando llegue el evento. El hilo de una ejecución puede esperar a tu
                  // cliente mientras la sesión sigue en ejecución.
                  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) {
                      // El servidor rechaza un resultado que llega demasiado tarde, después de que
                      // archivó el hilo de la llamada. Sigue siguiendo la ejecución.
                      IO.println("  Answer to " + toolUse.name() + " refused: " + e.getMessage());
                  }
              }
              case SESSION_STATUS_IDLE -> {
                  // Termina cuando todas las ejecuciones han finalizado y el agente ha terminado su turno
                  var stopReason = event.asSessionStatusIdle().stopReason();
                  if (openRuns.isEmpty() && stopReason.isEndTurn()) {
                      break events;
                  }
                  // Un idle con requires_action espera a tu cliente, así que sigue leyendo.
                  // Ante cualquier otro motivo de parada, imprímelo y detente.
                  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]

  // Abre primero el stream y luego envía el mensaje del usuario
  $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:
              // Responde cuando llegue el evento. El hilo de una ejecución puede esperar a tu
              // cliente mientras la sesión sigue en ejecución.
              $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) {
                  // El servidor rechaza un resultado que llega demasiado tarde, después de que
                  // archivó el hilo de la llamada. Sigue la ejecución.
                  echo "  Answer to {$event->name} refused: {$error->getMessage()}", PHP_EOL;
              }
              break;
          case $event instanceof \Anthropic\Beta\Sessions\Events\ManagedAgentsSessionStatusIdleEvent:
              // Termina cuando todas las ejecuciones han finalizado y el agente ha terminado su turno
              $finished = $event->stopReason->type === 'end_turn';
              if (!$openRuns && $finished) {
                  break 2;
              }
              // Un idle con requires_action espera a tu cliente, así que sigue leyendo.
              // Ante cualquier otro motivo de parada, imprímelo y detente.
              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

  # Abre primero el stream y luego envía el mensaje del usuario
  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
      # Responde cuando llegue el evento. El hilo de una ejecución puede esperar a tu
      # cliente mientras la sesión sigue en ejecución.
      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
        # El servidor rechaza un resultado que llega demasiado tarde, después de archivar
        # el hilo de la llamada. Sigue la ejecución.
        puts "  Answer to #{event.name} refused: #{error.message}"
      end
    when Anthropic::Beta::Sessions::BetaManagedAgentsSessionStatusIdleEvent
      reason = event.stop_reason.type.to_sym
      # Termina cuando todas las ejecuciones hayan finalizado y el agente haya terminado su turno
      break if open_runs.empty? && reason == :end_turn
      # Un idle con requires_action espera a tu cliente, así que sigue leyendo.
      # Ante cualquier otro motivo de parada, imprímelo y detente.
      unless %i[end_turn requires_action].include?(reason)
        puts "Session idle: #{reason}"
        break
      end
    when Anthropic::Beta::Sessions::BetaManagedAgentsSessionStatusTerminatedEvent
      break
    end
  end
  ```
</CodeGroup>

## Interrumpir una sesión con ejecuciones abiertas

Envía [`user.interrupt`](https://platform.claude.com/docs/es/managed-agents/events-and-streaming#interrupt-the-agent) sin `session_thread_id` (java: `sessionThreadId`; php: `sessionThreadID`; csharp, go: `SessionThreadID`), o con el ID del hilo principal. Detiene el turno del agente. No termina ninguna ejecución. Las ejecuciones de la sesión podrían pausarse o seguir en curso, y sus eventos podrían no mostrar cuál de las dos. El tiempo de vida de una ejecución pausada sigue transcurriendo, por lo que la ejecución puede terminar con `timeout_error` mientras está pausada.

* **Llamadas a herramientas en espera:** Después de la interrupción, una llamada a herramienta de un hilo de ejecución podría seguir esperando a tu cliente. Responde a cada una. Para cancelar una llamada que pide confirmación, deniégala. Para cancelar una llamada a herramienta personalizada, envía un resultado con `is_error` (java, php: `isError`; csharp, go: `IsError`) establecido en `true` y un texto en `content` (csharp, go: `Content`) que explique por qué. Mientras la sesión está `idle` con `requires_action`, un `user.message` devuelve 400, así que responde primero a las llamadas.
* **Para detener las ejecuciones:** Envía un `user.message` pidiéndole al agente que detenga sus ejecuciones. Una ejecución detenida termina con `result` `{"type": "stopped"}`. Mientras la sesión está `idle` con `budget_reached`, un `user.message` devuelve 400 hasta que aumentes o elimines el presupuesto. Aumentarlo o eliminarlo también reanuda las ejecuciones que el presupuesto pausó, a menos que la interrupción también las haya pausado.
* **Para continuar:** Envía un `user.message` pidiéndole al agente que continúe sus ejecuciones. Después de una interrupción, las ejecuciones podrían esperar este mensaje. Si la sesión está `idle` con `budget_reached`, primero aumenta o elimina el presupuesto.
* **Resultados de las ejecuciones:** Una ejecución que termina después de la interrupción sigue enviando `workflow_run.status_ended`.

## Mientras una ejecución está abierta

| Solicitud                                                                                           | Mientras una ejecución está abierta                                                                                                                                                                                                                                                                                                    | Qué hacer                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| --------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Archivar o eliminar la sesión                                                                       | Podría devolver 400 mientras una ejecución está abierta, sea cual sea el estado de la sesión. El `error.details.error_code` del error puede ser `"workflow_run_open"`. También podría tener éxito.                                                                                                                                     | Pídele al agente que detenga sus ejecuciones, o espera hasta que cada ejecución haya terminado. Una ejecución pausada termina por sí sola únicamente cuando se cumple su tiempo de vida. Luego envía la solicitud una vez que la sesión esté `idle`. Un archivado exitoso termina cada ejecución abierta con `{"type": "stopped"}`. Después de un archivado, el `workflow_run.status_ended` de una ejecución, y el `workflow_run.phase_ended` de una fase que seguía abierta, no llegan al flujo. Lista los eventos de la sesión para leerlos. Después de una eliminación exitosa, ningún evento `workflow_run` informa el final de las ejecuciones de la sesión.                                                                                                                                                                                                                                                                                                                                                               |
| Archivar uno de los hilos de una ejecución                                                          | Devuelve 400 con `error.details.error_code: "workflow_run_open"` mientras la ejecución está abierta, en ejecución o inactiva, a menos que el servidor ya haya archivado el hilo.                                                                                                                                                       | Nada. El servidor archiva los hilos de una ejecución.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| Actualizar el `agent` (csharp, go: `Agent`) de la sesión                                            | Devuelve 400 con `error.details.error_code: "workflow_run_open"` mientras cualquier ejecución está abierta, incluso una pausada. Actualizar el agente subyacente sigue aceptándose, y la sesión conserva su propia copia. Una solicitud que también envía otros campos, como `budget` (csharp, go: `Budget`), se rechaza por completo. | Espera hasta que cada ejecución tenga su `workflow_run.status_ended`, o pídele al agente que detenga sus ejecuciones.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| Responder a una llamada a herramienta o a una confirmación de herramienta del hilo de una ejecución | Permitido. Llega al flujo principal, y su `session_thread_id` (java: `sessionThreadId`; php: `sessionThreadID`; csharp, go: `SessionThreadID`) nombra el hilo.                                                                                                                                                                         | Responde en cuanto llegue el evento, pasando el `id` (csharp, go: `ID`) del evento como `tool_use_id` (java: `toolUseId`; php: `toolUseID`; csharp, go: `ToolUseID`) o `custom_tool_use_id` (java: `customToolUseId`; php: `customToolUseID`; csharp, go: `CustomToolUseID`). No esperes a `session.status_idle`: la sesión puede permanecer `running` mientras los demás hilos de la ejecución trabajan. Una vez que el servidor ha archivado el hilo, un resultado de herramienta para una de sus llamadas no tiene efecto y puede devolver 400. Cuando un resultado de herramienta devuelve 400, busca el hilo de la llamada en la lista de hilos. Si su estado es `terminated`, el resultado llegó demasiado tarde, así que descártalo. Envía cada resultado de herramienta en una solicitud propia, porque el servidor rechaza una solicitud completa cuando rechaza uno de sus eventos. Una confirmación de herramienta que llega demasiado tarde devuelve 200, lo que no significa que la herramienta se haya ejecutado. |

### Reconstruir el estado de la ejecución después de reconectarte

Reconstruye el estado de cada ejecución a partir de los eventos de la sesión. El flujo no reproduce lo que te perdiste: una nueva conexión entrega solo los eventos emitidos después de que se abrió. Así que lista los eventos con un filtro `types` (curl: `types[]`; cli: `--type`; csharp, go: `Types`), con una entrada para cada tipo de evento, como en [Listar eventos anteriores](https://platform.claude.com/docs/es/managed-agents/events-and-streaming#list-past-events). Pasa el `next_page` (ruby: `next_page_`; java, php: `nextPage`; csharp, go: `NextPage`) de cada respuesta como `page` (cli: `--page`; csharp, go: `Page`) hasta que `next_page` (ruby: `next_page_`; java, php: `nextPage`; csharp, go: `NextPage`) sea `null` (python: `None`; ruby: `nil`; java: `Optional.empty()`; go: `""`) o no esté presente. `workflow_run.created`, `workflow_run.status_running`, `workflow_run.status_idle` y `workflow_run.status_ended` dan el estado de cada ejecución, excepto que una ejecución pausada después de una interrupción podría seguir apareciendo como en ejecución. `workflow_run.phase_started` y `workflow_run.phase_ended` reconstruyen el progreso. Una ejecución que aún no tiene ningún evento de estado todavía no ha comenzado a ejecutarse. Ningún endpoint lista las ejecuciones.

## Costo y presupuestos

Las solicitudes al modelo de una ejecución cuentan para el [presupuesto de la sesión](https://platform.claude.com/docs/es/managed-agents/budgets). Una ejecución no tiene precio propio. Los tokens que usan sus agentes se facturan como los demás tokens de la sesión, a las tarifas de cada modelo. Para conocer todos los cargos de una sesión, consulta [Precios de Claude Managed Agents](https://platform.claude.com/docs/es/about-claude/pricing#claude-managed-agents-pricing).

* **Uso de una ejecución:** Lista los hilos de la sesión y suma los recuentos de tokens en `usage` (csharp, go: `Usage`) de los hilos con el `workflow_run_id` (java: `workflowRunId`; php: `workflowRunID`; csharp, go: `WorkflowRunID`) de la ejecución. La lista incluye los hilos archivados, cuyo estado es `terminated`, por lo que se cuentan los hilos de una ejecución finalizada. Pasa el `next_page` (ruby: `next_page_`; java, php: `nextPage`; csharp, go: `NextPage`) de cada respuesta como `page` (cli: `--page`; csharp, go: `Page`) hasta que `next_page` (ruby: `next_page_`; java, php: `nextPage`; csharp, go: `NextPage`) sea `null` (python: `None`; ruby: `nil`; java: `Optional.empty()`; go: `""`) o no esté presente, y omite cualquier hilo sin `usage` (csharp, go: `Usage`). Si en su lugar sumas el `list_cost` (java, php: `listCost`; csharp, go: `ListCost`) de los hilos, el total excluye el tiempo de ejecución de la sesión, y cada cifra se redondea por separado.
* **Al alcanzar el presupuesto:** Todas las ejecuciones abiertas se pausan, y la sesión informa `idle` con `budget_reached`, o `requires_action` si también hay una llamada a herramienta en espera. Cada hilo termina la solicitud al modelo que ya había iniciado, por lo que una ejecución puede superar el presupuesto en una solicitud por cada hilo que esté trabajando. Aumentar o eliminar el presupuesto reanuda las ejecuciones que pausó, a menos que una interrupción también las haya pausado. Si el uso de la sesión incluye un modelo sin precio de lista, solo eliminar el presupuesto las reanuda; consulta [Modelos sin precio de lista](https://platform.claude.com/docs/es/managed-agents/budgets#models-without-a-list-price). El tiempo de vida de una ejecución pausada sigue transcurriendo, por lo que la ejecución puede terminar con `timeout_error` mientras está pausada.

## Límites

| Límite                                                                      | Valor                                                                          | Al alcanzar el límite                                                                                                                                                                                                                                        |
| --------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Hilos trabajando a la vez en una ejecución                                  | 64                                                                             | La ejecución no crea más hasta que uno termine. La API no garantiza este número.                                                                                                                                                                             |
| Agentes que inicia un flujo de trabajo durante toda la vida de la ejecución | 1,000                                                                          | Cuando el flujo de trabajo pide más, el servidor no inicia otro agente, y la ejecución termina con `thread_limit_error`. El servidor puede volver a ejecutar un agente que falló en un hilo nuevo, por lo que una ejecución podría tener más de 1,000 hilos. |
| Tiempo de vida de la ejecución                                              | 24 horas de forma predeterminada, o el tiempo de vida que establezca el agente | La ejecución termina con `timeout_error`. Ningún evento indica qué tiempo de vida estableció el agente.                                                                                                                                                      |
| Ejecuciones abiertas a la vez en una sesión                                 | 10 de forma predeterminada                                                     | El servidor se niega a iniciar otra ejecución. La llamada a herramienta del agente recibe un error, y tú recibes un `workflow_run.error` cuyo `error.type` es `max_workflow_runs_error`. Las ejecuciones inactivas cuentan para el límite.                   |

El servidor acorta el `name` (csharp, go: `Name`) de una ejecución o fase a 64 caracteres y su `description` (csharp, go: `Description`) a 256. El servidor tiene otros límites sobre los flujos de trabajo, y reglas para ellos, que no se enumeran aquí. Lo que ves depende de cuándo el servidor detecta el problema:

| Qué ocurre                                                                                                                        | Qué ves                                                                                   |
| --------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
| El flujo de trabajo supera uno de los otros límites cuando el agente inicia la ejecución                                          | Se rechaza el inicio. Recibes un `workflow_run.error`, y ninguna ejecución.               |
| La ejecución supera uno de los otros límites más adelante                                                                         | Recibes un `workflow_run.error`, y luego la ejecución puede terminar con `unknown_error`. |
| El servidor detecta después del inicio que el flujo de trabajo infringe una regla de los flujos de trabajo, distinta de un límite | Recibes un `workflow_run.error`, y luego la ejecución puede terminar con `program_error`. |

Una sesión puede iniciar cualquier número de ejecuciones a lo largo de su vida.

* **Iniciar ejecuciones:** Solo el agente en el hilo principal de la sesión inicia ejecuciones. Un agente que trabaja en el hilo de una ejecución no puede iniciar una ejecución propia, por lo que las ejecuciones no se anidan.
* **Finalizar ejecuciones:** Ningún evento que envíes finaliza una ejecución; archivar la sesión sí puede hacerlo.
* **Los hilos de una ejecución:** No puedes [detener uno por su ID](https://platform.claude.com/docs/es/managed-agents/session-threads#interrupt-a-session-thread), ni archivar uno mientras su ejecución está abierta.
* **Límite de hilos:** Los hilos de una ejecución están exentos del [límite de hilos hijos](https://platform.claude.com/docs/es/managed-agents/session-threads#primary-thread-and-session-threads) de la sesión.
* **Visibilidad:** No ves el código del flujo de trabajo, pero puedes pedirle el flujo de trabajo al agente, como describe el consejo en [Los hilos de una ejecución](https://platform.claude.com/docs/es/managed-agents/workflow-runs#a-runs-threads). Tampoco ves las llamadas a herramientas que hace el agente para iniciar y gestionar ejecuciones, ni el resultado que cada hilo devuelve al flujo de trabajo.

### Límites de velocidad

El trabajo de una ejecución cuenta para los "rate limits" (límites de velocidad) que tu organización ya tiene.

| Qué                                                                                      | Cuenta para                                                                                                                                                    | Qué hacer                                                                                                                                                     |
| ---------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Las solicitudes de tu cliente para recuperar o listar la sesión, sus hilos y sus eventos | El límite de lectura de los [endpoints de Managed Agents](https://platform.claude.com/docs/es/managed-agents/reference#rate-limits)                            | Sigue una ejecución en el flujo de eventos de la sesión en lugar de hacer sondeos.                                                                            |
| Solicitudes al modelo de los hilos de una ejecución                                      | Tus [límites de velocidad de la Messages API](https://platform.claude.com/docs/es/api/rate-limits) para el modelo que usa cada hilo, junto con tu otro tráfico | Deja margen para una ejecución en esos límites, o [solicita límites más altos](https://platform.claude.com/docs/es/api/rate-limits#requesting-higher-limits). |

Cuando una solicitud al modelo de uno de los hilos de una ejecución alcanza un límite de velocidad, o el modelo está sobrecargado, el propio flujo del hilo puede recibir un `session.error` de tipo `model_rate_limited_error` o `model_overloaded_error`:

* Si su `retry_status.type` es `retrying`, el servidor está reintentando la solicitud y el hilo sigue trabajando.
* Si es `exhausted`, el hilo ha fallado. Si el flujo de trabajo deja que ese fallo termine la ejecución, la ejecución termina con `program_error`, que no nombra la causa. Lee los eventos de los hilos que fallaron para encontrarla.

El servidor también limita cuánto hacen por minuto todas las sesiones de tu organización. Un hilo que alcanza este límite se detiene, con un `session.error` en su propio flujo cuyo mensaje nombra un límite de velocidad. Espera un minuto antes de pedirle al agente que continúe.
