Skip to content

[FEATURE]: Allow messages to be sent mid-turn — interrupt, queue, or inject during active generation #21388

@dylandru

Description

@dylandru

Problem

Today, once a turn begins (model is streaming tokens or executing tool calls), the input is effectively frozen. Any message typed by the user is either:

  1. Queued silently and delivered as a new turn after the current one finishes
  2. Discarded in some UI states
  3. Never acknowledged — the user has no feedback about what will happen to it

This creates several painful failure modes:

  • The agent is mid-stream realizing the wrong approach, user can't correct it
  • The agent has started a 15-tool refactor in the wrong directory — no way to redirect without full cancel
  • The user wants to give additional context that changes the trajectory ("actually use the v2 API not v1") but must wait for the entire turn to finish
  • Cancel (Escape/Ctrl+C) is the only option — a blunt instrument that loses all progress

This is distinct from #17691 which proposes a /btw slash command specifically to inject context between tool calls. This issue is about a first-class mid-turn messaging system at the protocol level — covering all phases of a turn.


Desired Behavior

The user's input should always be live. Messages sent during an active turn should be handled in one of three modes (configurable per user / per session):

Mode 1: Queue + Inject at next tool boundary

Message is held and injected into the agent's context at the next natural breakpoint (between tool calls). The current tool finishes. No interruption.

Mode 2: Preempt — pause streaming, inject, resume

The model stream is paused mid-generation. The message is prepended to the pending assistant context and the turn resumes with the new context. Requires SSE stream cancellation + context splicing.

Mode 3: Hard interrupt with context carry-forward

The current turn is cancelled (existing behavior), but the accumulated partial tool results + the new user message are forwarded into a new turn automatically. The user doesn't lose the work done so far.


Proposed UX

┌─────────────────────────────────────────────────────┐
│  Agent is running... [Cancel ✕]  [Inject ↩]         │
│                                                     │
│  > _______________________________________________  │
│    Send mid-turn (injects at next tool boundary)    │
└─────────────────────────────────────────────────────┘
  • Input remains enabled during agent turns (not grayed out)
  • A visual indicator shows the message will be injected, not queued for next turn
  • Optional: show the queued message in the turn timeline as a "pending injection"

Technical Sketch

TUI path:

  • Keep input active at all times
  • On Enter during active turn: push to mid_turn_queue: Message[] on the session
  • Before each tool dispatch in runTurn(), drain mid_turn_queue → append as user messages to the message array
  • Surface queue state in the turn event stream: { type: \"mid_turn_message_queued\", content }

Web path:

  • Same session-level queue, surfaced via SSE event
  • Input stays active, send button changes to "Queue for injection" label during active turn

API path:

POST /session/{id}/message
{
  \"content\": \"actually use the v2 API\",
  \"delivery\": \"inject\" | \"queue\" | \"interrupt\"
}
  • inject: deliver at next tool boundary in current turn
  • queue: deliver as new turn after current completes (existing behavior)
  • interrupt: cancel current turn and start new one with carry-forward context

Why This Matters

This is one of the highest-leverage UX improvements for agentic workflows:

  • Long tasks become steerable instead of all-or-nothing
  • Reduces wasted compute — no more watching a wrong refactor complete before you can correct it
  • Enables collaborative human-in-the-loop workflows where the human and agent work together within a single turn
  • Naturally extends automode — external scripts can inject context mid-loop without cancelling

Related Issues

Metadata

Metadata

Assignees

Labels

coreAnything pertaining to core functionality of the application (opencode server stuff)

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions