Skip to navigation

Concurrency modes across agent frameworks

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:

ModeHeaderBehavior on a busy conversation
rejectx-runtype-concurrency: reject (default)Refuses the request with code CONVERSATION_BUSY and names the running execution.
supersedex-runtype-concurrency: supersedeCancels the running execution and starts a new one with this message.
queuex-runtype-concurrency: queueStarts 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.
Coalescex-runtype-concurrency: queue and x-runtype-coalesce: trueMerges 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.
steerx-runtype-concurrency: steerAdds 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.
autox-runtype-concurrency: autoA 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. For auto, including the classifier’s fallbacks and agent configuration, see Route a busy message automatically.

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.

CodeStatusCause
CONVERSATION_BUSY409The conversation is busy and the mode is reject.
CONVERSATION_QUEUE_FULL409An execution is already waiting, and the request is queue without coalesce, or steer.
INVALID_CONCURRENCY_MODE400On POST /v1/dispatch, the header value isn’t reject, supersede, queue, steer, or auto.
INVALID_STEER_ADMISSION400A steer request has no conversationId, doesn’t name a saved agent, targets a flow, or also sends x-runtype-coalesce: true.
STORED_HISTORY_UNSUPPORTED400A 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.

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

FrameworkChecked againstTheir termWhat it doesRuntype mode
Eve0.66.3turnPolicy: "steer" (default)Adds the message to the running turn and keeps the turn IDsteer
Eve0.66.3turnPolicy: "queue"Waits for the turn to finish and can fold adjacent waiting messages into one turnqueue with x-runtype-coalesce: true
Flue2.0.3Normal dispatchAdds the message to the active response at a tool or model boundarysteer
LangGraph (LangSmith Deployment)LangSmith docsenqueue (default)Runs the incoming input after the current run finishesqueue (one waiting message)
LangGraph (LangSmith Deployment)LangSmith docsrejectRefuses new input while a run is activereject
LangGraph (LangSmith Deployment)LangSmith docsinterruptStops the run, keeps its progress, and continues with the incoming inputsupersede
LangGraph (LangSmith Deployment)LangSmith docsrollbackStops the run, removes its progress and input, and starts freshNo equivalent
MastraMastra docssendMessage()Adds the message to the active loop, or starts a run when idlesteer
MastraMastra docsqueueMessage()Runs the message after the current run finishesqueue (one waiting message)
MastraMastra docsSession.steer()Stops the run, clears follow-ups, and sends the incoming messagesupersede
Cloudflare Agents (@cloudflare/ai-chat, @cloudflare/think)agents 0.24.0messageConcurrency: "queue" (default)Runs each submit in orderqueue (one waiting message)
Cloudflare Agentsagents 0.24.0"merge"Combines overlapping submits into one model turnqueue with x-runtype-coalesce: true
Cloudflare Agentsagents 0.24.0"drop"Ignores overlapping submits and doesn’t store themreject (Runtype returns an error instead of discarding silently)
Cloudflare Agentsagents 0.24.0"latest"Runs only the newest overlapping submit and stores the others without running themNo equivalent
Cloudflare Agentsagents 0.24.0{ strategy: "debounce" }Same as "latest", after a quiet windowNo equivalent
OpenAI Agents SDKagents-core 0.18.0, Python 0.22.3No text-agent policyYour application controls overlapAny mode
OpenAI Agents SDKagents-core 0.18.0, Python 0.22.3Realtime interrupt()Cancels the response in progress (voice barge-in)supersede
Claude Managed Agentsmanaged-agents-2026-04-01user.interrupt then user.messageStops the running turn, then starts the next turn with the incoming messagesupersede

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

Next steps