Skip to content

[FEATURE]:Configurable mid-run prompt delivery: queue vs steer, with compaction-aware steer semantics #32157

Description

@ytfh44

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:

  1. A new independent task:

    • “After this, also update the README.”
    • “Next, write tests for this module.”
    • “When you finish, run the benchmark.”
  2. 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:

    • normal
    • queue
    • steer
    • break
  • 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.

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions