Skip to content

fix: deliver queued input and confirmations in non-TUI IOProxy - #697

Merged
dwash96 merged 2 commits into
cecli-dev:v1.11.0from
chrisnestrud:fix/non-tui-queued-input
Nov 1, 2026
Merged

dwash96 merged 2 commits into
cecli-dev:v1.11.0from
chrisnestrud:fix/non-tui-queued-input

Conversation

@chrisnestrud

@chrisnestrud chrisnestrud commented Sep 26, 2026 •

Copy link
Copy Markdown

Problem

With tui: false, IOProxy.get_input delegated straight to the base
InputOutput terminal prompt and never drained the per-coder queue that
IOProxy.__init__ registers. So input pushed via queues.push_coder_input(...)
from the WebSocket server (cecli/helpers/server/ws_server.py), the ACP adapter
(cecli/helpers/server/acp/protocol.py), or AgentService.wake_primary
(cecli/helpers/agents/service.py) was silently dropped. confirm_ask had the
same gap, dropping queued confirmations. tui: true was unaffected because
TextualInputOutput.get_input drains the queues.

This also contradicted wake_primary's docstring, which assumes the idle primary
blocks in wait_for_input().

Fix

Non-TUI IOProxy.get_input now:

  1. Sweeps this coder's queue first (non-blocking).
  2. When nothing is waiting and a cancellable prompt session is present, races the
    terminal read against the per-coder queue wake, so a push is delivered even
    while the prompt is open.
  3. Falls back to a plain blocking read for the InterruptibleInput/input()
    path, which cannot be cancelled (marked with a ponytail: note naming the
    ceiling).

Confirmations take the same route and reuse the TUI payload mapping
(never/tweak/all/skip/bool) so both modes agree.

Why this shape

The wake event in cecli/helpers/queues.py is global, so the race loop
re-checks this coder's own queue after each wake instead of trusting a single
signal. A payload meant for a different reader is put back so its own waiter
still consumes it.

Tests

tests/subagents/test_io_proxy.py: queued text returns (text, uuid) and logs
user_input; a push is delivered while the terminal prompt is open; a
confirmation is left for confirm_ask by get_input; queued true/false
confirmations return the expected values.

Verified offline: tests/subagents/test_io_proxy.py 16 passed,
tests/unit/test_queues.py 15 passed; black (--line-length 100 --preview),
isort, pycodestyle, pyflakes, codespell clean; interactive tui=false re-prompt
loop unchanged (no regression).

Related

Documentation follow-up for this change: #698 (documents the non-TUI
input contract; merge after this PR).

Root cause: IOProxy.get_input only honored a coder's input queue in TUI
mode. In non-TUI mode it delegated straight to the base InputOutput terminal
prompt, so answers pushed onto a coder's queue by the WebSocket server, the ACP
adapter, or AgentService.wake_primary were silently dropped. confirm_ask had
the same gap, so queued confirmations were dropped too.

Fix: in non-TUI mode, sweep this coder's queue first. If no payload is waiting
and a cancellable prompt session is present, race the terminal read against the
per-coder queue wake so a push is delivered even while the prompt is open. The
global InterruptibleInput/input() fallback stays a plain blocking read (marked
with a ponytail: note) because it cannot be cancelled; a push arriving during
that read is delivered on the next get_input() cycle.

Shape rationale: the wake event in helpers/queues is global, so the race loop
re-checks this coder's own queue after every wake rather than trusting a single
signal. A payload for a different reader is put back so its own waiter can
consume it. Confirmations reuse the same path and share the TUI payload mapping
(never/tweak/all/skip/bool) so both modes agree.

Tests cover queued text, a push delivered while the prompt is open, a
confirmation left for confirm_ask by get_input, and queued true/false
confirmations.
@chrisnestrud
chrisnestrud force-pushed the fix/non-tui-queued-input branch from ce830f2 to e0d50ed Compare September 26, 2026 12:55
@dwash96
dwash96 changed the base branch from main to v1.11.0 November 1, 2026 14:59
dwash96 added a commit that referenced this pull request Nov 1, 2026
Replace the PR #697 race loop with a queue helper that scans the
per-coder queue once and restores payloads meant for other readers in
their original order. Non-TUI prompts poll that helper while the
terminal read is outstanding.

This avoids the ordering deadlock in the original one-pop implementation
and the global wake event's lost-wakeup window, awaits the cancelled
prompt before returning, keeps a partially typed prompt in placeholder,
and shares the TUI confirmation mapping while normalising string answers.

Cover the ordering cases, prompt-open delivery, draft stashing, special
confirmation responses, and the linear coder prompt; document the
non-TUI queue contract and inbound WebSocket payloads.

The merged PR #697 commit is retained in history for attribution.
@dwash96
dwash96 merged commit 192821f into cecli-dev:v1.11.0 Nov 1, 2026
1 check failed
@dwash96 dwash96 mentioned this pull request Nov 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants