OpenHands / OpenHands/software-agent-sdk

Introduce an explicit interrupt / steer / queue control plane for user messages (ACP parity)

Open
#4,829 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

acp architecture enhancement proposal
Dominant language
Python
Stars
1.1k
Forks
539
Avg merge
1d 19h
Merged PRs (30d)
137

Description

Note: this issue was opened by an AI agent and its terminology was initially inverted (it had OpenHands native "queueing" by default). Corrected by @enyst to match the terminology agreed in the #proj-agent-canvas discussion. The three verbs and the support matrix below are the authoritative version.

Context

There is currently no first-class, user-facing decision for what happens when a user sends a message while an agent turn is already running. The behavior is implicit and inconsistent across surfaces.

Terminology (Codex App terms)

Three distinct behaviors, ordered by how disruptive they are:

  1. interrupt — interrupt the current tool call and post the message now. The in-flight action is cancelled and the new message becomes the head.
  2. steer — wait for the current tool call to finish, then inject the message into the same running turn. Non-destructive: it does not kill in-flight work; it just gets the message to the agent as soon as the current step ends.
  3. queue — hold the message until the agent is FINISHED / IDLE (the whole turn is done and the agent has yielded control back to the user), then send it as the prompt for the next turn.

The key distinction between steer and queue is when the held message is delivered: steer = at the next step boundary, still inside the current turn; queue = only once the turn has fully finished.

Current support
Surface interrupt steer queue
OpenHands native (LocalConversation) yes yes no
OpenHands ACP (ACPAgent) yes (kill-by-default) no no
Codex (reference) yes yes (CLI default) yes
  • OpenHands native supports interrupt and steer, but not queue. send_message() appends a MessageEvent and rewinds FINISHED/STUCK -> IDLE, so the run loop picks the message up at the next step boundary (steer). There is no mode that holds a message until the turn is FINISHED and only then starts a fresh turn (queue).
  • OpenHands ACP effectively interrupts by default, routing intake through session/cancel and killing in-flight work. It supports neither steer nor queue.
  • Codex supports all three; in the Codex CLI steer is the default (configurable), and it also offers queue.

The gap / feature request

The behavior users are asking for (see the originating request) is queue — keep the current run intact, hold follow-ups, and deliver them one by one when the agent reaches FINISHED/IDLE, with interrupt kept as an explicit action rather than the default. OpenHands native has no queue mode today.

The end-of-turn queue is what enables the "give the agent a large task, line up several continue messages, walk away, and let each one fire when the agent yields plain text" workflow (self-driving long-running tasks). It became genuinely useful with GPT-5.x-class models. See the earlier write-up in OpenHands/OpenHands-CLI#226 ("Queue prompt at the end of turn?").

Proposal

Make the choice explicit rather than an emergent side effect of the lock/status race, and route all agent types (native, ACP) through one shared control plane exposing the three verbs above:

  • Add a queue primitive to OpenHands native (hold until FINISHED/IDLE, then start the next turn), alongside the existing steer and interrupt.
  • Bring ACP onto the same control plane so it can steer and queue instead of only interrupting.
  • Let the surface (or the user, per message) choose the mode; keep interrupt as an explicit action, not the default.

Related / adjacent

  • OpenHands/OpenHands-CLI#226 — "Queue prompt at the end of turn?" (the original request for the queue behavior).
  • OpenHands/software-agent-sdk#270 — "Enable concurrent message handling in agent conversations" (root feature).
  • OpenHands/software-agent-sdk#336 — "queue user messages in agent conversation" (earlier queue implementation attempt).
  • OpenHands/OpenHands#15432 — "messages sent during streaming are not always seen by the agent" (message-loss bug; makes the existing steer path unreliable in the async loop).
  • #4194 — fix(sdk): pick up a user message that arrives during an async step (closes #15432; correctness of the steer path, not a new mode).
  • #4674 — Split ConversationState's single FIFOLock by responsibility (structural prerequisite for intake-while-running).
  • OpenHands/OpenHands#15912 — ACP lifecycle control (pause / resume / cancel); to be expressed in interrupt / steer / queue terms.
  • OpenHands/OpenHands#16119 — deferred / pending user-message UI projection (queue rendering).

Scope note

This is a control-plane / conversation-semantics concern, filed here (rather than OpenHands/OpenHands) with the same routing rationale as #4674: no canvas-side code is required for the core model, though the frontend will need to expose the choice.


Originally created by an AI agent (OpenHands); terminology corrected by @enyst.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by locating LocalConversation, ACPAgent, send_message(), ConversationState, and the ACP session/cancel path; read the related issues for the existing intake and lifecycle behavior. Map how messages arrive during a running turn before proposing the shared control plane. Done means interrupt, steer, and queue have the stated delivery semantics for native and ACP agents, with the choice exposed to the relevant surface.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
backend-api-design
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.