← Back to PRs

#20456: fix(control-ui): show device pairing approval prompt when a new device requests access

by mmaghsoodnia open 2026-02-18 23:44 View on GitHub →
app: web-ui size: M
## Summary Fixes #20447 When a new browser/device requests pairing, the gateway broadcasts `device.pair.requested` to all connected Control UI clients with `operator.pairing` scope. Previously this event only silently refreshed the device list (`loadDevices({ quiet: true })`), giving the already-connected operator UI no visible indication that action was needed. - **Root cause:** `app-gateway.ts` handled `device.pair.requested` the same way as `device.pair.resolved` — just a quiet background list refresh. No queue, no overlay, no badge. - **Fix:** Mirror the `exec.approval.requested` pattern: queue incoming requests in `devicePairingQueue` and render them as an overlay prompt that lets the operator approve or reject without navigating away. Queue entry is removed when `device.pair.resolved` is received (regardless of decision) or when the operator acts via the prompt. ### Files changed | File | Change | |------|--------| | `ui/src/ui/views/device-pair-prompt.ts` | New overlay component (reuses `exec-approval-*` CSS classes) | | `ui/src/ui/app-gateway.ts` | Populates/clears `devicePairingQueue` on events; resets on reconnect | | `ui/src/ui/app-view-state.ts` | Adds `devicePairingQueue`, `devicePairBusy`, `devicePairError`, `handleDevicePairDecision` to view state type | | `ui/src/ui/app.ts` | Adds reactive state fields and `handleDevicePairDecision` method | | `ui/src/ui/app-render.ts` | Renders `renderDevicePairPrompt(state)` alongside the exec approval prompt | ## Test plan - [ ] Open Control UI in browser A (paired) - [ ] Open Control UI in browser B (new profile, no keypair) - [ ] Browser A should show the device pairing overlay immediately — device name, role, IP - [ ] Click **Approve** → overlay disappears, browser B connects - [ ] Repeat and click **Reject** → overlay disappears, browser B shows pairing-required error - [ ] Multiple pending requests queue correctly (counter badge shown) - [ ] Reconnecting clears the queue (`devicePairingQueue = []` on `connectGateway`) - [ ] All 896 existing tests pass (`npx vitest run`) 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- greptile_comment --> <h3>Greptile Summary</h3> Added device pairing approval prompt to Control UI by mirroring the existing exec approval pattern. When `device.pair.requested` events arrive, they're now queued in `devicePairingQueue` and rendered as an overlay that lets operators approve or reject without navigating away. The queue is cleared on reconnect and items are removed when `device.pair.resolved` events arrive or when the operator makes a decision. <h3>Confidence Score: 5/5</h3> - This PR is safe to merge with minimal risk - The implementation correctly mirrors the existing exec approval pattern with consistent queue management, event handling, and UI rendering. All state is properly initialized and cleared on reconnect. No logical errors or race conditions detected. - No files require special attention <sub>Last reviewed commit: ad9af1d</sub> <!-- greptile_other_comments_section --> <sub>(2/5) Greptile learns from your feedback when you react with thumbs up/down!</sub> <!-- /greptile_comment -->

Most Similar PRs