Feature 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:
TUI behavior:
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:
-
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.
queue does not interrupt the current run and becomes the next normal turn.
steer affects the active run at a safe boundary.
steer does not create a standalone compaction unit.
- Compaction preserves steer messages as constraints/modifiers attached to the target task.
break remains 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
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.
Feature hasn't been suggested before.
Describe the enhancement you want to request
Summary
Please add a first-class distinction between
queue,steer, andbreakfor 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
breakmode.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:
A steering correction for the current task:
packages/server.”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:
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:
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.steeris 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:
Suggested semantics:
queueWhen the session is busy:
steerWhen the session is busy:
breakKeep 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:
Allow a default mode plus per-submit override.
Programmatic API:
or separate explicit APIs:
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:
normalqueuesteerbreakFor
steer, also store a target:Update compaction turn grouping:
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
steeras 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:
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:
Events could also expose delivery semantics:
Acceptance criteria
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.breakremains available as explicit interrupt/abort behavior.Related
This request is specifically about making
steera distinct semantic delivery mode, because steering input is usually not a complete task and should not be compacted like one.