Skip to content

fix(cli): serialize subagent confirmation focus to prevent concurrent input conflicts#2930

Open
pic4xiu wants to merge 3 commits intoQwenLM:mainfrom
pic4xiu:fix/subagent-focus-lock
Open

fix(cli): serialize subagent confirmation focus to prevent concurrent input conflicts#2930
pic4xiu wants to merge 3 commits intoQwenLM:mainfrom
pic4xiu:fix/subagent-focus-lock

Conversation

@pic4xiu
Copy link
Copy Markdown

@pic4xiu pic4xiu commented Apr 6, 2026

TLDR

When multiple subagents run in parallel and each triggers a confirmation prompt, all prompts simultaneously receive keyboard focus (isFocused={true} is hardcoded in AgentExecutionDisplay). This means pressing ↑/↓/Enter dispatches to every active confirmation at once, causing unintended approvals.

This fix introduces a "first-come, first-served" focus lock so only one confirmation is interactive at a time, while others show "⏳ Waiting for other approval...".

Screenshots / Video Demo

Before (bug) — all three prompts show on option 2 simultaneously; pressing ↑/↓ moves the selection in all prompts at once:

  ╭─────────────────────────────────────────────────────────────────────────────╮
  │ ⊷  Agent Fetch httpbin.org/get                                            │
  │     ?  web_fetch Fetching content from https://httpbin.org/get ...         │
  │     Do you want to proceed?                                               │
  │       1. Yes, allow once                                                  │
  │     › 2. Allow always                                                     │
  │       3. No                                                               │
  │                                                                           │
  │ ⊷  Agent Write content to test file                                       │
  │     ?  write_file Writing to /tmp/test-agent-b.txt                        │
  │     Do you want to proceed?                                               │
  │       1. Yes, allow once                                                  │
  │     › 2. Allow always                                                     │
  │       3. No                                                               │
  │                                                                           │
  │ ⊷  Agent Echo hello to file                                               │
  │     ?  run_shell_command echo "hello from agent C" > /tmp/test-agent-c... │
  │     Do you want to proceed?                                               │
  │       1. Yes, allow once                                                  │
  │     › 2. Allow always                                                     │
  │       3. No                                                               │
  ╰─────────────────────────────────────────────────────────────────────────────╯

After (fixed) — only the first prompt is focused ( on option 2); the other two show "⏳ Waiting for other approval..." and their selections are frozen at option 1:

  ╭─────────────────────────────────────────────────────────────────────────────╮
  │ ⊷  Agent Fetch httpbin.org/get                                            │
  │     ?  web_fetch Fetching content from https://httpbin.org/get ...         │
  │     Do you want to proceed?                                               │
  │       1. Yes, allow once                                                  │
  │     › 2. Allow always                                                     │
  │       3. No                                                               │
  │                                                                           │
  │ ⊷  Agent Write content to test file                                       │
  │     ?  write_file Writing to /tmp/test-agent-b.txt                        │
  │     ⏳ Waiting for other approval...                                      │
  │     Do you want to proceed?                                               │
  │     › 1. Yes, allow once                                                  │
  │       2. Allow always                                                     │
  │       3. No                                                               │
  │                                                                           │
  │ ⊷  Agent Echo hello to file                                               │
  │     ?  run_shell_command echo "hello from agent C" > /tmp/test-agent-c... │
  │     ⏳ Waiting for other approval...                                      │
  │     Do you want to proceed?                                               │
  │     › 1. Yes, allow once                                                  │
  │       2. Allow always                                                     │
  │       3. No                                                               │
  ╰─────────────────────────────────────────────────────────────────────────────╯

Dive Deeper

In AgentExecutionDisplay.tsx, ToolConfirmationMessage was rendered with isFocused={true} hardcoded. When multiple subagents run in parallel and each triggers a confirmation, all prompts simultaneously capture keyboard input.

The fix introduces a focus lock mechanism in ToolGroupMessage:

  1. Type guard (isAgentWithPendingConfirmation): cleanly identifies subagent tool calls with pending confirmations, replacing scattered as casts.
  2. useRef-based lock: the first subagent to present a confirmation acquires focus. The lock is released only when that confirmation is resolved, then automatically promotes to the next pending subagent.
  3. Prop passthrough: isFocused flows down ToolGroupMessageToolMessageSubagentExecutionRendererAgentExecutionDisplayToolConfirmationMessage.
  4. Waiting indicator: non-focused confirmations render "⏳ Waiting for other approval..." so users understand why they can't interact yet.
  5. Priority: tool-level confirmations (toolAwaitingApproval) always take priority over subagent-level confirmations.

isFocused defaults to true in AgentExecutionDisplay, so all existing call sites are unaffected. No new dependencies introduced.

Modified files:

  • packages/cli/src/ui/components/messages/ToolGroupMessage.tsx — Core focus-lock logic (type guard, useRef, isSubagentFocused computation)
  • packages/cli/src/ui/components/messages/ToolMessage.tsx — Prop passthrough (isFocused?: boolean)
  • packages/cli/src/ui/components/subagents/runtime/AgentExecutionDisplay.tsx — Consume isFocused, render waiting indicator, forward to ToolConfirmationMessage

Reviewer Test Plan

  1. Launch a session that triggers 3 subagents in parallel (e.g., "Launch 3 subagents: one fetches a URL, one writes a file, one runs a shell command")
  2. Wait for all three subagents to each reach a confirmation prompt
  3. Verify that only the first confirmation prompt has an active selection indicator ()
  4. Verify that the other two prompts show "⏳ Waiting for other approval..."
  5. Resolve the focused confirmation → verify focus automatically moves to the next pending one
  6. Test with a single subagent to confirm no regression (behaves exactly as before)

Testing Matrix

🍏 🪟 🐧
npm run
npx
Docker
Podman - -
Seatbelt - -

Linked issues / bugs

Fixes #2929

@pic4xiu pic4xiu marked this pull request as ready for review April 6, 2026 11:47
Comment on lines 174 to 181
{!isFocused && (
<Box marginBottom={0}>
<Text color={theme.text.secondary} dimColor>
⏳ Waiting for other approval...
</Text>
</Box>
)}
<ToolConfirmationMessage
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] isFocused is currently carrying two meanings: generic UI focus and ownership of the serialized approval lock. In AgentExecutionDisplay, any !isFocused renders ⏳ Waiting for other approval..., so the UI can claim another approval is blocking interaction even when this message is simply unfocused.

Consider passing a dedicated prop for the approval-lock state (for example isWaitingForOtherApproval or hasConfirmationFocusLock) and only showing the waiting message when another approval actually holds the lock.

Copy link
Copy Markdown
Collaborator

@wenshao wenshao left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR fixes the parallel subagent confirmation focus bug by serializing focus through the relevant CLI UI components.

One review comment was left: the new waiting-state message currently conflates generic component focus with ownership of the serialized approval lock, which can make the UI claim it is waiting on another approval even when it is merely unfocused.

Overall verdict: Comment.

@pic4xiu
Copy link
Copy Markdown
Author

pic4xiu commented Apr 7, 2026

@wenshao Thanks for the feedback! I've addressed your suggestion in commit d7c92e2 by introducing a dedicated isWaitingForOtherApproval prop in AgentExecutionDisplay.tsx to decouple the focus state from the approval waiting state.

The UI now only shows the "⏳ Waiting for other approval..." indicator when another subagent actually holds the confirmation lock, rather than relying on !isFocused. This ensures the waiting message is semantically accurate and doesn't conflate generic focus with lock ownership.

Could you please take another look when you have a chance? Thanks!

Copy link
Copy Markdown
Collaborator

@wenshao wenshao left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found. LGTM! ✅

Reviewed by gpt-5.4 via Qwen Code /review

思晗 added 2 commits April 7, 2026 16:31
…t conflicts

When multiple subagents run in parallel and each triggers a confirmation
prompt, all prompts previously received keyboard focus simultaneously,
causing a single keypress to be dispatched to every active confirmation.

This change introduces a first-come-first-served focus lock mechanism:
- Track subagents with pending confirmations via a type guard
- Use a useRef-based lock so only one confirmation is focused at a time
- Automatically promote focus to the next pending subagent on resolution
- Show a waiting indicator on non-focused confirmations

Fixes QwenLM#2929
@pic4xiu pic4xiu force-pushed the fix/subagent-focus-lock branch from d7c92e2 to df09141 Compare April 7, 2026 08:31
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.

Multiple concurrent subagent confirmations all receive keyboard focus, causing input conflicts

2 participants