How to Control Message Delivery with Steering vs Sequential Queueing in Copilot SDK

Use session.rpc.messages.enqueue() for FIFO sequential processing and session.rpc.steering.steerMessage() for immediate injection into the current turn, contingent on the remote session mode being set to "on".

Controlling when and how user messages reach the agent is critical for building responsive Copilot SDK applications. The github/copilot-sdk provides two distinct delivery mechanisms—sequential queueing and steering—that determine whether a message waits in line or interrupts the active generation. Understanding these modes allows you to implement everything from batched command processing to real-time mid-turn corrections.

Understanding Sequential Queueing (FIFO)

Sequential queueing is the default delivery mode where messages are appended to a first-in, first-out (FIFO) queue. The runtime processes each message only after the current turn completes, ensuring strict ordering for conversational flows and batched commands.

According to the SDK source code in nodejs/src/generated/rpc.ts, sequential queueing uses the EnqueueMessageParams interface with mode: "enqueue" (lines ~1800). When you call the enqueue method, the SDK constructs a QueueEnqueueMessageResult and appends the payload to the PendingItems.items array.

// Sequential queueing – the default FIFO behavior
await session.rpc.messages.enqueue({
  content: "Please list the files in the workspace.",
  // mode defaults to "enqueue"
});

The message now resides in session.rpc.queue.pendingItems().items and will execute only after preceding queued items finish. This mode is appropriate for standard conversational turns where maintaining message order matters.

Understanding Steering (Immediate Injection)

Steering bypasses the queue entirely by injecting a message into the live turn currently being processed. Instead of waiting for the current generation to finish, the runtime interjects the payload immediately via the steeringMessages lane.

This mechanism relies on the SteerMessageParams interface defined in nodejs/src/generated/rpc.ts (lines ~20700-20730). When you invoke steerMessage, the SDK places the content into PendingItems.steeringMessages, allowing the model to react without a full turn boundary.

// Immediate steering – interject into the current turn
await session.rpc.steering.steerMessage({
  addressableItemId: undefined, // Optional: ID of queued item to replace
  content: "Actually, focus on `src/utils` only.",
});

Use steering when you need urgent corrections, rapid clarifications, or UI-driven "undo" operations that must affect the generation in progress. Note that steering only functions when the session's remoteSteerable flag is enabled.

Configuring Remote Steering Permissions

The SDK can disable steering entirely via the SessionMode.remote setting, found in nodejs/src/types.ts (lines 2583-2584). When set to "off", any steerMessage call becomes a no-op: it returns success but the steeringMessages array remains empty, as validated in nodejs/test/e2e/rpc_server_remote_control.e2e.test.ts.

// Disable remote steering (steering becomes no-op)
await session.rpc.session.setRemoteSteerable({
  remote: "off", // disables both export and steering
});

// Re-enable steering
await session.rpc.session.setRemoteSteerable({
  remote: "on", // enables export + steering
});

The remote setting accepts three values:

  • "on" – Full steering and export capabilities enabled
  • "export" – Export-only mode (steering disabled)
  • "off" – All remote control disabled (steering calls ignored)

How the Runtime Processes Messages

Internally, the SDK maintains a PendingItems snapshot that distinguishes between queued and steered messages. As defined in nodejs/src/generated/rpc.ts (lines ~12231-12233), the interface separates these into distinct lanes:

interface PendingItems {
  items: QueuedMessage[];          // Normal FIFO queue (sequential)
  steeringMessages: string[];     // Immediate-steering lane
  // …other metadata
}

When remoteSteerable is true, the runtime checks the steeringMessages array during active generation and interjects those payloads immediately. If remoteSteerable is false, the array stays empty despite successful API calls, and messages follow only the sequential path.

Practical Implementation Example

Combine these APIs to build a "quick edit" feature that interrupts the current assistant response:

async function quickEdit(session: Session, newPrompt: string) {
  // Ensure steering is enabled
  await session.rpc.session.setRemoteSteerable({ remote: "on" });

  // Inject the correction immediately
  await session.rpc.steering.steerMessage({
    content: newPrompt,
  });

  // Verify the pending snapshot
  const pending = await session.rpc.queue.pendingItems();
  console.log("Queue length:", pending.items.length);
  console.log("Steering lane:", pending.steeringMessages);
}

This pattern first confirms that steering is permitted, then inserts the message directly into the active turn without waiting for queue processing.

Summary

  • Sequential queueing is the default FIFO mode accessed via session.rpc.messages.enqueue(), suitable for ordered conversational flows and batched commands.
  • Steering provides immediate injection via session.rpc.steering.steerMessage(), placing messages in the steeringMessages lane for mid-turn interruption.
  • Remote permissions control steering availability through session.rpc.session.setRemoteSteerable(); when set to "off", steering calls succeed but have no effect.
  • Internal structure in nodejs/src/generated/rpc.ts defines PendingItems with separate items and steeringMessages arrays to track both delivery paths.

Frequently Asked Questions

What happens if I call steerMessage when remote steering is disabled?

The call returns a success response, but the message is ignored. The SDK treats it as a no-op, leaving the steeringMessages array empty in the PendingItems snapshot. This behavior is verified in the test suite nodejs/test/e2e/rpc_server_remote_control.e2e.test.ts.

Can I use both sequential queueing and steering in the same session?

Yes. You can enqueue messages to the FIFO queue while simultaneously steering messages into the current active turn. The two mechanisms operate on separate lanes within the PendingItems structure, allowing mixed delivery strategies within a single session.

How do I check if steering is currently enabled?

Query the current session mode using session.rpc.session.getRemoteSteerable(). Alternatively, inspect the SessionMode.remote property in nodejs/src/types.ts to determine whether the value is "on", "export", or "off" before attempting to steer.

What is the difference between addressableItemId and regular steering?

The addressableItemId parameter in SteerMessageParams allows you to target a specific queued item for replacement or modification. When omitted or set to undefined, the steering message inserts as a new interjection into the current turn without replacing a specific queued message.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →