> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.runtype.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.runtype.com/_mcp/server.

# Concurrency modes across agent frameworks

> Map the busy-conversation behavior of Eve, Flue, LangGraph, Mastra, Cloudflare Agents, OpenAI Agents SDK, and Claude Managed Agents to Runtype concurrency modes.

A user often sends a message while the agent is still answering the previous one. Every agent framework has a policy for that moment, but the names don't agree. "Steer" adds the message to the running turn in Eve and in Runtype, but cancels the turn in Mastra. "Interrupt" keeps the stopped run's progress in LangGraph and ends the turn in Claude Managed Agents.

This page maps each framework's term to the Runtype mode that behaves the same way. Use it to reproduce another framework's behavior on a Runtype agent, or to compare policies before you choose one.

## Runtype concurrency modes

You choose a mode per request with the `x-runtype-concurrency` header. The following table lists what each mode does when the conversation already has a running execution:

| Mode        | Header                                                        | Behavior on a busy conversation                                                                                                                                                                                                                                                                                                              |
| ----------- | ------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `reject`    | `x-runtype-concurrency: reject` (default)                     | Refuses the request with code `CONVERSATION_BUSY` and names the running execution.                                                                                                                                                                                                                                                           |
| `supersede` | `x-runtype-concurrency: supersede`                            | Cancels the running execution and starts a new one with this message.                                                                                                                                                                                                                                                                        |
| `queue`     | `x-runtype-concurrency: queue`                                | Starts a new execution after the running one finishes. A conversation holds one waiting execution. While one is waiting, another `queue` request returns `409` with code `CONVERSATION_QUEUE_FULL`.                                                                                                                                          |
| Coalesce    | `x-runtype-concurrency: queue` and `x-runtype-coalesce: true` | Merges this request's messages into the execution that's already waiting in the queue. If nothing is waiting, the request queues normally. Coalesce applies only to `queue`.                                                                                                                                                                 |
| `steer`     | `x-runtype-concurrency: steer`                                | Adds this message to the running execution at the next safe boundary. While a `queue` request is waiting, `steer` can't overtake it and returns `CONVERSATION_QUEUE_FULL`.                                                                                                                                                                   |
| `auto`      | `x-runtype-concurrency: auto`                                 | A classifier decides whether this message steers the running execution or queues after it. The answer reports the route in `X-Runtype-Busy-Message-Route` and the reason in `X-Runtype-Busy-Message-Source`. While an `auto` message is being classified or is waiting, another `queue` or `auto` request returns `CONVERSATION_QUEUE_FULL`. |

The modes apply to saved native Runtype agents on `POST /v1/dispatch` and `POST /v1/agents/{id}/execute`. Send the same `conversationId` on every message in the conversation. Without a `conversationId`, each request runs on its own: `reject`, `supersede`, and `queue` have no effect, and `steer` returns `INVALID_STEER_ADMISSION`. If an agent's configuration doesn't support these modes, Runtype ignores `reject`, `supersede`, and `queue`, and returns an error for `steer`. For the full `steer` contract, including delivery receipts, see [Steer an active agent execution](/developer-guides/guides/live-message-steering). For `auto`, including the classifier's fallbacks and agent configuration, see [Route a busy message automatically](/developer-guides/guides/busy-message-auto-routing).

Send an `Idempotency-Key` header that's unique for each user message. If a network retry repeats the request, Runtype returns the original execution's ID instead of queuing or superseding again, and the retry doesn't count against your quota.

In the TypeScript SDK, pass the headers as the `concurrency`, `coalesce`, and `idempotencyKey` options of `client.dispatch.execute()`, `executeAsync()`, and `executeStream()`.

### Refusals and errors

A request refused because of its concurrency headers returns one of the following codes. For the other errors that `steer` can return, see [Troubleshoot steer errors](/developer-guides/guides/live-message-steering#troubleshoot-steer-errors).

| Code                         | Status | Cause                                                                                                                            |
| ---------------------------- | ------ | -------------------------------------------------------------------------------------------------------------------------------- |
| `CONVERSATION_BUSY`          | `409`  | The conversation is busy and the mode is `reject`.                                                                               |
| `CONVERSATION_QUEUE_FULL`    | `409`  | An execution is already waiting, and the request is `queue` without coalesce, or `steer`.                                        |
| `INVALID_CONCURRENCY_MODE`   | `400`  | On `POST /v1/dispatch`, the header value isn't `reject`, `supersede`, `queue`, `steer`, or `auto`.                               |
| `INVALID_STEER_ADMISSION`    | `400`  | A `steer` request has no `conversationId`, doesn't name a saved agent, targets a flow, or also sends `x-runtype-coalesce: true`. |
| `STORED_HISTORY_UNSUPPORTED` | `400`  | A `steer` request also sends `history: "stored"`. Send only the new user messages with `steer`.                                  |

The `CONVERSATION_BUSY` body names the running execution's `executionId` and its `phase`: `claiming`, `running`, `paused`, or `queued`. The response carries a `Retry-After` header of one second. When the phase is `queued`, a `steer` retry returns `CONVERSATION_QUEUE_FULL`. For what each phase means, see [Steer a conversation that answered busy](/developer-guides/guides/live-message-steering#steer-a-conversation-that-answered-busy).

On a streaming request, a `CONVERSATION_BUSY` or `CONVERSATION_QUEUE_FULL` refusal doesn't use an HTTP error status. Runtype returns a `200` event stream that contains a single error event with the refusal `code` and the running execution's ID. Check the stream's error event, not only the status code.

### Browser clients

Browser clients use the `submitMode` field on `POST /v1/client/chat` instead of the header:

* **`normal`** (default) and **`interrupt`**: supersede the running execution. The two values behave the same way. Runtype records which one the client sent.
* **`steer`**: adds the message to the running execution. Send `durableRecovery: true` on `POST /v1/client/init`, and offer `steer` only when the response returns `durableRecovery.steer: true`. That requires durable turns turned on for the chat surface or client token. For setup, see [Steer from a browser client](/developer-guides/guides/live-message-steering#steer-from-a-browser-client).
* **`auto`**: lets a classifier steer the message into the running execution or queue it as the next turn. It takes the same message shape as `steer`, and with nothing running it starts an ordinary turn.

## Map a framework's term to a Runtype mode

The following table maps each framework's policy to the Runtype mode with the same behavior. The **Checked against** column records the release or documentation that this mapping was checked against, because several frameworks have changed these semantics between releases. For the page or commit behind each row, see [Sources](#sources).

| Framework                                                      | Checked against                   | Their term                              | What it does                                                                       | Runtype mode                                                       |
| -------------------------------------------------------------- | --------------------------------- | --------------------------------------- | ---------------------------------------------------------------------------------- | ------------------------------------------------------------------ |
| Eve                                                            | 0.66.3                            | `turnPolicy: "steer"` (default)         | Adds the message to the running turn and keeps the turn ID                         | `steer`                                                            |
| Eve                                                            | 0.66.3                            | `turnPolicy: "queue"`                   | Waits for the turn to finish and can fold adjacent waiting messages into one turn  | `queue` with `x-runtype-coalesce: true`                            |
| Flue                                                           | 2.0.3                             | Normal dispatch                         | Adds the message to the active response at a tool or model boundary                | `steer`                                                            |
| LangGraph (LangSmith Deployment)                               | LangSmith docs                    | `enqueue` (default)                     | Runs the incoming input after the current run finishes                             | `queue` (one waiting message)                                      |
| LangGraph (LangSmith Deployment)                               | LangSmith docs                    | `reject`                                | Refuses new input while a run is active                                            | `reject`                                                           |
| LangGraph (LangSmith Deployment)                               | LangSmith docs                    | `interrupt`                             | Stops the run, keeps its progress, and continues with the incoming input           | `supersede`                                                        |
| LangGraph (LangSmith Deployment)                               | LangSmith docs                    | `rollback`                              | Stops the run, removes its progress and input, and starts fresh                    | No equivalent                                                      |
| Mastra                                                         | Mastra docs                       | `sendMessage()`                         | Adds the message to the active loop, or starts a run when idle                     | `steer`                                                            |
| Mastra                                                         | Mastra docs                       | `queueMessage()`                        | Runs the message after the current run finishes                                    | `queue` (one waiting message)                                      |
| Mastra                                                         | Mastra docs                       | `Session.steer()`                       | Stops the run, clears follow-ups, and sends the incoming message                   | `supersede`                                                        |
| Cloudflare Agents (`@cloudflare/ai-chat`, `@cloudflare/think`) | agents 0.24.0                     | `messageConcurrency: "queue"` (default) | Runs each submit in order                                                          | `queue` (one waiting message)                                      |
| Cloudflare Agents                                              | agents 0.24.0                     | `"merge"`                               | Combines overlapping submits into one model turn                                   | `queue` with `x-runtype-coalesce: true`                            |
| Cloudflare Agents                                              | agents 0.24.0                     | `"drop"`                                | Ignores overlapping submits and doesn't store them                                 | `reject` (Runtype returns an error instead of discarding silently) |
| Cloudflare Agents                                              | agents 0.24.0                     | `"latest"`                              | Runs only the newest overlapping submit and stores the others without running them | No equivalent                                                      |
| Cloudflare Agents                                              | agents 0.24.0                     | `{ strategy: "debounce" }`              | Same as `"latest"`, after a quiet window                                           | No equivalent                                                      |
| OpenAI Agents SDK                                              | agents-core 0.18.0, Python 0.22.3 | No text-agent policy                    | Your application controls overlap                                                  | Any mode                                                           |
| OpenAI Agents SDK                                              | agents-core 0.18.0, Python 0.22.3 | Realtime `interrupt()`                  | Cancels the response in progress (voice barge-in)                                  | `supersede`                                                        |
| Claude Managed Agents                                          | `managed-agents-2026-04-01`       | `user.interrupt` then `user.message`    | Stops the running turn, then starts the next turn with the incoming message        | `supersede`                                                        |

## Terms that mean different things

Three words cause most of the confusion. Check the framework, not the word, before you pick a Runtype mode.

* **Steer**: Runtype `steer` and Eve's `steer` both add input to the running turn. Mastra's `Session.steer()` stops the run first, so it maps to `supersede`. Before release 0.57.0, Eve's `steer` also cancelled the turn, so older Eve documentation describes the `supersede` behavior. Flue and Mastra `sendMessage()` provide the same behavior as Runtype `steer` without using the word.
* **Interrupt**: LangGraph's `interrupt` and Claude Managed Agents' `user.interrupt` both map to `supersede`. The Runtype browser `submitMode: "interrupt"` also supersedes. LangGraph's graph-level `interrupt()` function is a different feature: it pauses a run for human input and isn't a concurrency policy.
* **Queue**: Every framework in the table runs queued input after the current turn. They differ on whether several waiting messages run as separate turns or merge into one. Runtype holds one waiting turn per conversation. `queue` alone refuses a second waiting message with `CONVERSATION_QUEUE_FULL`, and `x-runtype-coalesce: true` merges it into the waiting turn.

## Differences within a matching mode

A matching mode doesn't guarantee identical timing. The following differences affect what the user sees:

* **Steer timing**: When a model request is still waiting for its first output, Eve stops that request and sends it again with the incoming message included. Runtype `steer` lets an in-flight model call or tool batch finish, then applies the message at the next boundary.
* **Queue depth**: LangGraph `enqueue`, Mastra `queueMessage()`, and Cloudflare `"queue"` accept several waiting messages and run them in order. Runtype `queue` holds one. To accept more, add `x-runtype-coalesce: true` to merge them, or retry after `CONVERSATION_QUEUE_FULL`.
* **Busy response**: LangGraph `reject` and Runtype `reject` both refuse the request. Runtype also returns the running execution ID and its phase, so a client can switch to `steer` without guessing.

## Behaviors with no Runtype equivalent

The following policies from other frameworks have no Runtype mode. If your application depends on one, handle it in your client:

* **Rollback** (LangGraph): removes the stopped run's progress and its input from the conversation before it runs the incoming message.
* **Newest message only** (Cloudflare `"latest"` and `"debounce"`): stores older overlapping messages but runs only the newest one.
* **Store without running** (Mastra `persist` signal behavior): adds a message to an idle thread without starting a run.
* **Separate active and idle policies** (Mastra `ifActive` and `ifIdle`): applies one behavior when a run is active and another when the thread is idle. In Runtype, `steer` on an idle conversation always starts an execution.

## Reproduce a framework's behavior

To make a Runtype agent behave like a framework policy, send the header from the **Runtype mode** column of the mapping table on every message.

For example, the following requests reproduce Eve's `queue` policy. Each one queues the message and merges it with any message that's already waiting:

**`TypeScript SDK`**

```typescript title="TypeScript SDK"
import { RuntypeClient } from '@runtypelabs/sdk'

const client = new RuntypeClient({ apiKey: process.env.RUNTYPE_API_KEY! })
const result = await client.dispatch.execute(
  {
    agent: { agentId: 'agent_YOUR_AGENT_ID' },
    conversationId: 'conversation-123',
    messages: [{ role: 'user', content: 'Also compare the delivery times.' }],
  },
  { concurrency: 'queue', coalesce: true, idempotencyKey: 'user-message-42' }
)
```

**`cURL`**

```bash title="cURL"
curl https://api.runtype.com/v1/dispatch \
  -H "Authorization: Bearer $RUNTYPE_API_KEY" \
  -H 'Content-Type: application/json' \
  -H 'x-runtype-concurrency: queue' \
  -H 'x-runtype-coalesce: true' \
  -H 'Idempotency-Key: user-message-42' \
  -d '{
    "agent": { "agentId": "agent_YOUR_AGENT_ID" },
    "conversationId": "conversation-123",
    "messages": [
      { "role": "user", "content": "Also compare the delivery times." }
    ]
  }'
```

Replace the following:

* `agent_YOUR_AGENT_ID`: the ID of your saved native agent
* `RUNTYPE_API_KEY`: your Runtype API key
* `conversation-123`: any stable ID that you reuse for every message in the conversation
* `user-message-42`: a key that's unique for each user message

The Claude Managed Agents row reproduces an explicit interrupt followed by a message. The Claude Managed Agents documentation doesn't state what a `user.message` sent during a running turn does without an interrupt, so this page doesn't map that case.

## Sources

Each mapping was checked on 2026-09-25 against the following sources. Where a framework's documentation carries no version, the link points at the page or the pinned commit that was read:

* **Eve 0.66.3**: [Execution model and durability](https://github.com/vercel/eve/blob/d54fd760624de1fe67c865dbb7b8787cecbb8b8c/docs/concepts/execution-model-and-durability.mdx) and the [Eve changelog](https://github.com/vercel/eve/blob/d54fd760624de1fe67c865dbb7b8787cecbb8b8c/packages/eve/CHANGELOG.md) (release 0.57.0 changed `steer`).
* **Flue 2.0.3**: [Agent API](https://github.com/withastro/flue/blob/832ad2eeaf5e4b07d39749fc669e7ad556238313/apps/docs/src/content/docs/reference/agent-api.md).
* **LangGraph**: [Double texting](https://docs.langchain.com/langsmith/double-texting) in the LangSmith documentation.
* **Mastra**: [Signals](https://mastra.ai/docs/harness/signals) and the [`Session.steer()` implementation](https://github.com/mastra-ai/mastra/blob/62da1b231af65b86a367fb221d8a183bbecea6c8/packages/core/src/agent-controller/session.ts#L3598-L3605).
* **Cloudflare Agents 0.24.0**: [`MessageConcurrency`](https://github.com/cloudflare/agents/blob/930949cd39f02d6b804ed836e39be12cb23a6974/packages/agents/src/chat/lifecycle.ts#L350).
* **OpenAI Agents SDK**: [`RealtimeSession.interrupt()` in TypeScript](https://github.com/openai/openai-agents-js/blob/7b534bbfa2f78be0b815001daa076bb82508536b/packages/agents-realtime/src/realtimeSession.ts#L2108) and [`RealtimeSession.interrupt()` in Python](https://github.com/openai/openai-agents-python/blob/0f87da2bf16b6b1fbc8dd3d8f36f57d298475a5d/src/agents/realtime/session.py#L384).
* **Claude Managed Agents**: [Session event stream](https://platform.claude.com/docs/en/managed-agents/events-and-streaming).

## Next steps

* [Steer an active agent execution](/developer-guides/guides/live-message-steering): delivery receipts, error codes, and browser steering.
* [Route a busy message automatically](/developer-guides/guides/busy-message-auto-routing): let a classifier choose between `steer` and `queue`.
* [End-user identity and credentials](/developer-guides/guides/end-user-identity): keep scope and credential authority consistent across messages.