Steer a run without interrupting it
Watch a slow turn: where steering lands, when follow-ups arrive, and how clearQueue returns what Escape restores to the editor.
Typing while Pi works is not an interruption: a steering message enters after the
current turn’s tool calls and before the next request, a follow-up waits until the
agent would otherwise stop, and Escape aborts and returns your queued text. The
walkthrough below mirrors ch25-queue/queue.test.ts on 1.0.4, which queues
messages into a real session running a deliberately slow tool with a scripted
provider.
The timeline
turn 1 prompt "start"
the model asks for the slow tool; its tool calls run
you type steer "change direction" → queued, not yet readable by the model
turn_end after the last tool result
next request system | user:start | assistant | toolResult | user:change direction
turn 2 the model answers the correction
a follow-up queued earlier is still waiting
agent_settled nothing left outstanding → the follow-up is finally deliveredThe request shape in the middle is exactly what the test asserts: steering landed
after the tool result and before the next prompt (shape() in the test omits the
system message). One ordering you cannot guess from the keys: steering drains
before follow-ups, even when the follow-up was queued first.
The delivery rules
| Event | What happens |
|---|---|
steer() while a turn runs |
lands after that turn’s tool calls, before the next request |
followUp() |
delivered only when there are no tool calls or steering messages left |
prompt while streaming, no streamingBehavior |
rejected — an error, not a guess |
extension command (/status) while streaming |
runs immediately; the same text steered is refused |
queue_update carries the complete queue each time, not a delta — the test
asserts the second update still lists the first message and the last update is
empty. steeringMode and followUpMode each default to "one-at-a-time", which
delivers one steering message per turn; "all" delivers every queued message at
once.
Escape returns your text
clearQueue() returns the queued text — the test observes
{ steering: ["S1"], followUp: ["F1"] } — which is what a client restores into its
editor, and pendingMessageCount drops to zero. To reproduce Escape, send
clear_queue before abort, so the run stops with nothing left pending.
Mechanism and limitations
Documented: the two delivery points, the mode settings, queue_update, and the
clear_queue-then-abort ordering are documented RPC/SDK behaviour on 1.0.4.
Observed: queue.test.ts asserts every row above through a real session —
including the exact ["user:start","assistant","toolResult","user:change direction"]
shape, one-at-a-time vs all delivery, the complete-queue updates, and the
extension command that ran while streaming.
Everything here is about when a message is delivered, not whether the model
takes it: the provider is a scripted faux, so nothing on this page shows a real
model obeying a steering instruction. And the boundary is agent_settled, not
agent_end — retries, compaction recovery and queued work can still follow
agent_end, a distinction this queue example does not itself exercise.
Understand this example
Copy this prompt into your AI tool. No code runs here.
Apply this example
Copy this prompt into your AI tool. No code runs here.