Repository navigation
Support Queue and Steer while a response is running #23
Description
Request
When a response is already running, Hermex Android should let the user type another message and choose whether to:
- Queue it for the next turn, or
- Steer the active run without stopping it.
Today the only available action during a stream is Stop.
Investigation
This is a confirmed Android-client parity gap, not a WebUI Bridge API limitation.
At current Android master (60b056f6de69a14527caba26312f9ac4601c889c):
ChatViewModelexplicitly records steer-while-running as unimplemented. Its comment predates the now-available server contract.ChatComposerState.isTextFieldEnabledbecomes false wheneverisStreamingis true, and the trailing action becomesSTOP.ChatComposerapplies that disabled state to the text field and replaces Send with Stop.ChatViewModel.sendMessage()returns immediately while streaming, so there is no non-UI route for a pending message.HermexApiexposes chat start and cancel, but not steer.
No existing Android issue or PR covers Queue/Steer. Android #10 is adjacent but distinct: it fixed Stop followed by edit/resend racing cancellation and producing an active-stream 409.
Available server contract
Steer
Current WebUI supports authenticated POST /api/chat/steer:
{"session_id":"...","text":"..."}The response is HTTP 200 with:
{"accepted":true|false,"fallback":null|"<reason>","stream_id":"..."|null}Steer is real mid-run guidance: it calls agent.steer(text) without interrupting the active response. A rejected/unavailable Steer must preserve the draft and must not silently Queue, Stop, or start a normal turn. Relevant fallback reasons include no_cached_agent, agent_lacks_steer, session_not_found, not_running, stream_dead, and steer_error; gateway-owned runs can return the gateway-specific queued fallback.
Queue
Current WebUI master has no public /api/chat/queue endpoint. Browser Queue is a client-owned, session-keyed FIFO that later drains through /api/chat/start, so Android can implement Queue now with local persisted state keyed by both server and session.
Open WebUI PR #6761 proposes a server-authoritative /api/chat/queue, but it is not a stable Bridge contract yet. If it lands, Android should prefer that contract while retaining a capability/version fallback for older servers.
Android also needs to decode pending_steer_leftover: if a steer was accepted but the run finished before reaching a tool-result boundary, WebUI emits this SSE event so the text becomes the next queued normal turn instead of disappearing.
Shared product semantics are tracked in nesquena/hermes-webui#3058.
Expected behavior
- Keep the composer editable while a response is streaming.
- Keep Stop available while adding explicit Queue and Steer actions.
- Queue remains visibly pending and scoped to its originating server/session until it drains, is edited/deleted, or is upgraded to Steer.
- Steer calls
/api/chat/steer, shows an accepted/delivered state, and does not create a duplicate normal user turn. - A rejected Steer keeps the user's text and explains the fallback.
- A
pending_steer_leftoverevent converts the leftover text into a visible queued next turn for the owning session.
Acceptance criteria
- The user can type while
isStreaming == true, with Queue/Steer/Stop all discoverable. - Add
ChatSteerRequest/ChatSteerResponseDTOs and an authenticatedHermexApi.chatSteer()call using the existing server cookie/client. - Successful Steer clears only the captured owning draft and is never rendered as a normal queued/user turn.
- Every HTTP/network/
accepted:falseSteer path restores or retains the draft for its owning session and leaves the active run untouched. - Queue uses a stable FIFO keyed by server ID and session ID, survives navigation, and drains one item only after the active stream settles and the server releases
active_stream_id. - Queue entries retain normal message context needed by
/api/chat/start(text, attachments, model/provider/profile selection, stable ID, and creation time). pending_steer_leftoveris decoded and queued once without duplicate drain.- Stop keeps its current cancel behavior; immediate Stop/Queue/Steer transitions do not regress the Stop mid-stream then edit + resend → 409 'session already has an active stream', resent message never appears #10 cancellation ordering fix.
- Tests cover active-run composer state, per-server/session isolation, persistence and drain ordering, no duplicate drain, Steer success and every fallback, session switching, leftover conversion, attachment ownership, auth propagation, and Stop/cancel races.
A text-only Steer first slice is acceptable because the endpoint itself accepts text. Queue should retain normal attachment metadata for its later /api/chat/start; full Steer attachment parity can follow by reusing the existing upload + attached-files marker flow.
Activity