Run Control
Cancel or steer an AiAgent run already in flight with cancelRun() and steerRun(), addressed purely by threadId — no token to construct or wire up.
Every AiAgent supports cancelling or steering a run already in flight — addressed purely by threadId, with nothing extra to construct or thread through your call sites.
🚀 Start a Conversation First
Run control is about an active run. A typical flow is:
Pick or receive a
threadId.Start the agent run with that
threadId.Reuse the same
threadIdfor follow-up turns.Call
cancelRun()orsteerRun()if that run needs intervention.
agent = aiAgent(
name : "Assistant",
instructions: "Be concise and helpful"
)
// Pick a stable thread ID for this conversation
threadId = "support-42"
reply1 = agent.run( "Hi, I need help with my invoice", {}, {
threadId : threadId,
userId : "user-123",
conversationId: "billing"
} )
// Same conversation thread: reuse the same IDs
reply2 = agent.run( "Can you summarize next steps?", {}, {
threadId : threadId,
userId : "user-123",
conversationId: "billing"
} )If you do not pass options.threadId, the agent auto-generates one per call. That is great for one-off calls, but you cannot target that run later unless you saved that generated ID.
🧵 What Is threadId?
threadId is the execution correlation key for a run.
It identifies which in-flight run
cancelRun()andsteerRun()should target.It links checkpointed/suspended work to the correct thread for resume flows.
It can be reused across turns to keep a conversation lane consistent.
Think of it as a conversation lane ID, not a message ID.
🎯 Core Run-Control Calls
🎯 How It Works
Both take effect at the run's next checkpoint — beforeLLMCall or beforeToolCall — not instantly. This is deliberate: a run is stopped or redirected between well-defined steps, never mid-flight through a provider call.
cancelRun( threadId, reason = "Cancelled by caller." )stops the run with a terminalAiMiddlewareResult.cancel( reason ).steerRun( threadId, input )splices a new message into the live request without restarting anything already in progress — nothing already produced is lost.
Both return false as a safe no-op when the given thread has no run currently in flight — there's nothing to check before calling either one.
🛠️ Practical In-Flight Examples
Cancel a running task
Steer a running task without restarting
Steer with a full message struct
✅ Choosing Good Thread IDs
Use IDs that are stable and unique per active conversation.
Good:
support-42,tenantA-user123-chat9,ticket-9981Avoid: random new ID for every turn (unless intentionally one-shot)
If your app is multi-tenant, include tenant/user dimensions in the thread ID or pair it with userId and conversationId consistently.
📡 Events
onAIAgentRunCancel / onAIAgentRunSteer fire with { agent, threadId, reason, input } — but only when the call actually affects a run in flight, never on the no-op case:
🔌 Powering Gateway Sessions
aiGatewaySession()'s steer and interrupt dispatch policies are built directly on steerRun()/cancelRun() — a GatewaySession calls exactly the same API you can call yourself:
See Gateway Sessions for the full policy table.
Related Pages
RunControlMiddleware (Internal) — internal cancellation/steering middleware details
Middleware Overview —
AiMiddlewareResult.cancel()and middleware flow
Last updated