Repository navigation
[FEATURE]:Configurable mid-run prompt delivery: queue vs steer, with compaction-aware steer semantics #32157
Description
Feature hasn't been suggested before.
- I have verified this feature I'm about to request hasn't been suggested before.
Describe the enhancement you want to request
Summary
Please add a first-class distinction between queue, steer, and break for user prompts submitted while a session is already running.
At minimum, I would like users to be able to choose between:
queue: keep the new prompt as the next normal user turn and run it after the current assistant turn finishes.steer: inject the new prompt as steering context for the currently running turn at the next safe boundary, without treating it as a standalone task.
The existing interrupt/abort behavior can remain as an explicit break mode.
This is related to #21388, but the main point here is not only mid-turn UX. The distinction matters for context integrity after compaction.
Problem
When a user sends a new prompt during an active run, the message can mean two very different things:
-
A new independent task:
- “After this, also update the README.”
- “Next, write tests for this module.”
- “When you finish, run the benchmark.”
-
A steering correction for the current task:
- “Actually use the v2 API, not v1.”
- “Wrong directory; use
packages/server.” - “Do not touch the legacy auth file.”
- “Keep going, but preserve the existing public interface.”
- “That error is expected; continue with the migration.”
The second kind is not a complete task. It is dependent context for the task that is already in progress.
If OpenCode stores or processes these steering messages as ordinary user prompts, it creates artificial turn boundaries. That interacts badly with compaction.
The current compaction model is naturally turn-oriented: roughly, a user prompt plus the assistant/tool behavior until the next user prompt becomes one semantic unit. That is correct for normal prompts and queued prompts. It is not correct for steering messages.
A steer message should belong to the active turn. It should not start a new compaction unit.
Why this matters
Consider this session:
User:
Refactor the auth middleware to use the new client.
Assistant:
Reads files, starts editing.
User, while the run is active:
Actually use `auth-v2.ts`; do not touch `legacy-auth.ts`.
Assistant:
Continues refactor.
The second user message is not a new task. It modifies the currently running task.
If compaction later treats that message as a standalone turn, the summary can lose the dependency relation:
- It may preserve “use auth-v2.ts” but lose what that instruction modifies.
- It may preserve the refactor but lose the constraint.
- It may split the assistant/tool behavior before and after the steer into separate fake tasks.
- It may retain one side of the turn as recent tail while summarizing away the other side.
After compaction, the model can no longer reliably reconstruct the intended task state.
This is different from queue. A queued prompt is a full next task, so it should start a normal user turn.
It is also different from break. A break interrupts or cancels the current execution and starts a new control flow. That can reasonably become a new turn boundary.
steer is neither of those. It is inline control context for the current turn.
Proposed behavior
Add a user-selectable mid-run prompt delivery mode.
Possible modes:
type MidRunPromptMode = "queue" | "steer" | "break"Suggested semantics:
queue
When the session is busy:
- Save the prompt as pending work.
- Do not cancel or redirect the current run.
- Submit it as the next normal user turn after the current run finishes.
- Treat it as a normal compaction boundary.
steer
When the session is busy:
- Record the input as steering context for the active turn.
- Inject it at the next safe boundary, for example before the next model call or after the current tool call completes.
- Do not treat it as an independent user task.
- Do not let it create a standalone compaction unit.
- Preserve its relationship to the active task during compaction.
break
Keep the current interrupt/abort behavior, but make it explicit and distinct from steer.
A break means the user wants to stop or replace the current run. A steer means the user wants to guide the current run.
Suggested UI/API surface
This could be exposed in a few layers.
User config:
{
"session": {
"midRunPrompt": "steer" // or "queue" / "break"
}
}TUI behavior:
-
Show the current mid-run delivery mode in the prompt area when the session is busy.
-
Add commands/keybinds:
- “Send as steer”
- “Queue after current turn”
- “Break and handle now”
-
Allow a default mode plus per-submit override.
Programmatic API:
session.prompt({
sessionID,
parts,
delivery: "queue" | "steer" | "break"
})or separate explicit APIs:
session.enqueue({ sessionID, parts })
session.steer({ sessionID, parts })
session.break({ sessionID, parts })Compaction requirements
The important part is that delivery semantics should survive into the message/session model.
A possible implementation direction:
-
Store a delivery type on user-submitted input:
normalqueuesteerbreak
-
For
steer, also store a target:- active assistant message ID, active user message ID, turn ID, or run ID.
-
Update compaction turn grouping:
- normal/queued/break prompts start user turns.
- steer prompts attach to their target turn.
-
Update summary instructions so steer messages are preserved as constraints/modifiers of the target task, not summarized as separate tasks.
-
Ensure “latest message / unprocessed task” logic does not mistake a steer for queued independent work.
The goal is not only to keep the text of the steer message. The goal is to preserve its attachment to the task it modifies.
Plugin API follow-up
After the core behavior is stable, please consider exposing steer as a plugin API.
This would be useful for downstream agent harnesses such as code-yeongyu/oh-my-openagent.
Plugins and orchestrators often need to add corrective or coordinating context during a run:
- “The planner selected this file; continue there.”
- “The reviewer found this constraint; apply it to the current edit.”
- “The workflow state changed; keep the current task but adjust the next step.”
- “The subagent result should steer the active agent, not become a new user task.”
Today, without a first-class steer API, downstream tools must approximate this with normal prompts, hooks, or interrupts. That creates the same compaction problem: orchestration context becomes fake user turns.
Possible plugin-facing shape:
await ctx.session.steer({
sessionID,
parts,
target: "current",
strategy: "next-boundary"
})
await ctx.session.enqueue({
sessionID,
parts
})Events could also expose delivery semantics:
session.prompt.submitted {
delivery: "queue" | "steer" | "break"
}
session.steer.injected {
targetTurnID
}
session.queue.flushed {}Acceptance criteria
- Users can choose the default behavior for prompts submitted while a session is busy.
- Users can override the behavior per prompt.
queuedoes not interrupt the current run and becomes the next normal turn.steeraffects the active run at a safe boundary.steerdoes not create a standalone compaction unit.- Compaction preserves steer messages as constraints/modifiers attached to the target task.
breakremains available as explicit interrupt/abort behavior.- The delivery type is visible enough in the session/event model for future plugin APIs.
- Existing behavior can remain as the default if needed for backward compatibility.
Related
- [FEATURE]: Allow messages to be sent mid-turn — interrupt, queue, or inject during active generation #21388: mid-turn messages: interrupt, queue, or inject.
- [FEATURE]: Improve compaction prompt to preserve critical context details #16512: compaction losing critical user constraints and technical detail.
This request is specifically about making steer a distinct semantic delivery mode, because steering input is usually not a complete task and should not be compacted like one.
Activity
github-actions commentedon Jun 13, 2026
This issue might be a duplicate of existing issues. Please check:
/btw command — inject context into agent mid-task without interrupting— very similar mid-task context injection conceptAllow forcing immediate reading of queued messages (steering)— directly overlaps with the steer delivery mode described hereprompts queue + change, cancel and forward— covers the queue/cancel/forward delivery modesPlease review these issues before proceeding, and consider whether this request can be consolidated with one of them.
tlerbao commentedon Jul 3, 2026
Adding a UX-focused +1 for this.
The main interaction I would like is the same split that tools like Codex expose for follow-up behavior while an agent is already running:
Entersubmits the follow-up asqueue, preserving the current run and handling the message as the next turn.Ctrl+Entersubmits the follow-up assteer, attaching it to the active run at the next safe boundary.{ "session": { "midRunPrompt": "queue" // or "steer" } }This would avoid overloading all mid-run messages as FIFO queued work. In practice, many follow-ups are corrective steering context, such as “wrong directory”, “use v2 API instead”, or “do not touch that file”; those are not independent next tasks and should not have to wait for the current run to finish.
Keeping
queueas the default for backward compatibility is fine, but having a per-submit override plus a config-level default would make the behavior much easier to control.felipeorlando commentedon Jul 30, 2026
The V2 runner now models queue and steer as distinct pending-input delivery modes (#38602), so the remaining gap appears to be the intended user-facing interaction and compaction contract.
Could a maintainer confirm whether a minimal V2 UX along these lines is desired?
If this direction is accepted, I can help test the current V2 behavior and contribute a focused implementation or regression coverage without expanding the scope into broader queue management.
benjaminbrumbaugh commentedon Aug 6, 2026
@felipeorlando Ctrl+Enter: steer the active task at the next safe boundary would perhaps be wrong on macOS. CMD+Enter is likely a better candidate for a TUI. But I could be convinced
benjaminbrumbaugh commentedon Aug 6, 2026
+1 Also Queue being the default not Steer for just 'Enter' is super important. Interrupt by default is bad
chaosmanage commentedon Aug 22, 2026
Partial overlap with this request is up in #44117: it restores the Queue vs Steer setting (per-session FIFO queue) that exists behind the scenes today. The fuller per-message delivery modes described here remain tracked by #33247. If you want queue mode back sooner, a 👍 on #44117 helps it get review.
anphex commentedon Aug 23, 2026
+1 — we just had a real incident caused by exactly this gap (OpenCode 1.18.21, openrouter/google/gemini-3.7-flash, reasoning high).
While the agent was in a long tool-call loop, I typed "what are you doing right now?" twice. Both messages were written to the session (visible in opencode.db as user rows inserted mid-stream), but since there is no queue/steer/break distinction they were just appended as the next user message after the next tool result — no priority, no wrapper, no turn boundary. The model never produced a text answer; its reasoning parts literally said "I was directly addressing your question", then it kept executing its previous plan with more tool calls. It escalated past a failing pre-push review gate (tried an env-var bypass, then wrote a fake review marker) and ran git push origin main. Only a double-Esc abort killed the push ~300 ms in. Its next reasoning step then falsely claimed the push had succeeded and started a production deploy sequence.
So from the user's perspective: my questions "arrived" but were effectively ignored, because the harness gives a mid-run prompt no semantics at all — the model alone decides whether to react. Stronger models tend to answer; Gemini Flash-class models treated it as noise.
What would have prevented this:
Happy to share the sanitized session dump if useful.
johnsonAyo commentedon Aug 31, 2026
+1 to this — I hit the exact case
steeris meant for in the CLI (TUI) today:The agent was working on a task, went off in the wrong direction, and I sent a corrective prompt. That prompt just sat in the queue while the agent kept going down the rabbit hole, because there's no way to say "deliver this now, as steering for the running turn". My only options were to wait it out or hit interrupt and resend — the queued message itself had no way to be promoted.
Concretely, what would fix this for me:
Steercontrol on the pending prompt in the TUI) that interrupts the current turn at a safe boundary and injects that queued message as steering input for the next turn — instead of waiting for the current (wrong) work to finish.steerdefault mode: I wantqueueto remain the default behavior, but with an explicit escape hatch per queued prompt when I can see the agent is going the wrong way.Happy to help prototype a patch for the TUI side of this (queued-prompt → steer promotion) if that's useful.
hrstoyanov commentedon Sep 27, 2026
Please do it , Codex is awesome with this feature
neriousy commentedon Oct 1, 2026
OpenCode v2 makes this distinction for prompts sent while a session is running:
ctrl+x return(prompt.queue) queues: the message waits until the agent is idle, and queued messages are delivered one at a time.A pending message can be moved between queue and steer, or deleted, before it's delivered. A steered compaction is handled before other pending prompts.
Added in #40922; the keybind moved to command IDs in #41882.
Closing as completed. If something doesn't work on v2, please open a new issue.
feat: add OpenCode adapter and V2 orchestration support (#49)