Surface the TUI's blocking dialogs in chat, and let them be answered there#319
Open
xerhab wants to merge 1 commit into
Open
Surface the TUI's blocking dialogs in chat, and let them be answered there#319xerhab wants to merge 1 commit into
xerhab wants to merge 1 commit into
Conversation
…there
Claude Code blocks a turn on two dialogs the chat could not see: a tool
permission request ("Bash command ... Do you want to proceed?") and a plan
approval. Neither has a hook to intercept it and neither writes anything to
the transcript, so the chat showed nothing at all.
Worse, while a dialog is up the pane shows NEITHER the "esc to interrupt"
hint NOR the mode footer, so paneBusy reads False and every surface labelled
the session idle. A session actually blocked on a human read as finished,
and the only way to see it - or answer - was the raw terminal. That was the
last routine reason to leave the chat page.
- parse_pane_prompt reads the dialog off the same capture as the mode
marker: {prompt, options:[{number,label,selected}], detail}. Nothing keys
on the wording (it differs per dialog); a line run qualifies only with
options numbered 1..N, exactly one ❯ cursor, a "?" line above, and no mode
footer below - the footer rides the composer a dialog replaces, so its
absence is what separates a live dialog from transcript text shaped like
one. detail carries the command + its description, or the plan body.
- Reported as session.panePrompt, rendered in the chat's pending-question
box with numbered picks, and answered via
POST .../sessions/<id>/pane-prompt {optionNumber} -> answerPanePrompt,
which types the digit. The agent RE-READS the pane first and drops the
answer unless that number is on screen: the click was made against a
beat-old heartbeat, and a stray digit typed into a live composer prepends
itself to the operator's next message.
- Both liveStates check it ahead of the busy read, so the card reads
"waiting for your answer" instead of idle, and Stop is hidden while a
dialog waits (same reasoning as XERK-21 for questions).
Verified: agent 803 + turma 634 green, with the parser and answer tests
driven by verbatim capture-pane output from two live dialogs rather than
hand-written strings. Exercised in Chromium end to end - the dialog renders
with its context and picks, clicking option 2 queues
answerPanePrompt{optionNumber:2}, the sidebar reports "1 waiting on you",
checked light/dark/390px. Android gap recorded in PARITY.md.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Second follow-up to #317 — the remaining routine reason to switch to terminal mode.
The problem
Claude Code blocks a turn on two dialogs the chat could not see, because neither has a hook to intercept it and neither writes anything to the transcript:
And it's worse than invisible: while a dialog is up the pane shows neither the "esc to interrupt" hint nor the mode footer, so
paneBusyreads False and every surface labelled the session idle. A session actually blocked on a human read as finished — so you'd only discover it by opening the terminal, which is also the only place you could answer.The change
parse_pane_promptreads the dialog off the same capture as the mode marker →{prompt, options:[{number,label,selected}], detail}, reported assession.panePrompt.❯cursor, a?line directly above, and no mode footer below. That last one is the discriminator — the footer rides the composer, which a dialog replaces, so its absence separates a live dialog from transcript text shaped like one.detailis the fenced block above the question — the command plus its description, or the plan body. Blanks never close it, a rule does: a permission dialog's block sits directly above the question while a plan's sits one rule further up, and one walk has to find both.POST .../sessions/<id>/pane-prompt {optionNumber}→answerPanePrompt→ the agent types that digit. The agent re-reads the pane first and drops the answer unless the number is on screen right now — the click was made against a beat-old heartbeat, and a bare digit typed into a live composer silently prepends itself to the operator's next message.liveStates (dashboard + sessions) check it ahead of the busy read, so the card reads "waiting for your answer" rather than idle; Stop is hidden while a dialog waits, same reasoning as XERK-21.Verification
tmux capture-paneoutput captured from two live dialogs (a real Bash permission request and a real plan approval on Claude Code 2.1.220), not hand-written strings — the wordings, glyphs and blank-line placement are the contract, and inventing them is how a parser passes its tests and fails a pane. Negative controls cover an idle composer showing a numbered list with a "?" above it, missing cursor, numbering not starting at 1, and a single option.answerPanePrompt {optionNumber: 2}on the agent's command queue, the sidebar header reports "1 running · 1 waiting on you" and the card reads "waiting for your answer" (it read plain idle before this change). Checked light, dark, and 390px.Notes
android/PARITY.md.CLAUDE.mdis over its own 150k budget (161k on main before this PR, ~163k after). Not introduced here and not something I wanted to fold into this change silently — worth a consolidation pass of its own.