From 3eaaa2ca3c5e7ef2bb6a7ffd106c82559583a0d7 Mon Sep 17 00:00:00 2001 From: Dallin Romney Date: Tue, 30 Jun 2026 18:10:04 -0700 Subject: [PATCH] fix(tui): correct disconnect copy for device scope upgrades (#98144) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(tui): correct disconnect copy for device scope upgrades On disconnect, the TUI told users "Pairing required. Run `openclaw devices list`, approve your request ID, then reconnect." This is misleading: the gateway is asking for a device *scope upgrade* (the device is already paired), and "pairing" points users at `openclaw pairing`, which only handles chat DM pairing — a different subsystem. - Reword the hint to name the scope upgrade and the actual recovery command (`openclaw devices approve --latest`), including the `--token`/`--password` escape hatch for when the device can't approve its own upgrade. - Also match the gateway's "scope upgrade" disconnect reason, not just "pairing required". AI-assisted (Claude Code). * fix(tui): clarify device approval preview hint --- src/tui/tui.test.ts | 19 ++++++++++++++++--- src/tui/tui.ts | 9 ++++++--- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/tui/tui.test.ts b/src/tui/tui.test.ts index 1f90313499df..1d728a1047eb 100644 --- a/src/tui/tui.test.ts +++ b/src/tui/tui.test.ts @@ -346,11 +346,24 @@ describe("resolveInitialTuiAgentId", () => { }); describe("resolveGatewayDisconnectState", () => { - it("returns pairing recovery guidance when disconnect reason requires pairing", () => { + it("returns scope-upgrade recovery guidance when disconnect reason requires pairing", () => { const state = resolveGatewayDisconnectState("gateway closed (1008): pairing required"); expect(state.connectionStatus).toContain("pairing required"); - expect(state.activityStatus).toBe("pairing required: run openclaw devices list"); - expect(state.pairingHint).toContain("openclaw devices list"); + expect(state.activityStatus).toBe("device approval needed: preview latest request"); + expect(state.pairingHint).toContain("openclaw devices approve --latest"); + expect(state.pairingHint).toContain("openclaw devices approve "); + expect(state.pairingHint).toContain("--token"); + // Must steer users to `devices`, not the unrelated chat-DM `pairing` command. + expect(state.pairingHint).not.toContain("openclaw pairing"); + }); + + it("returns the same guidance when the gateway reports a pending scope upgrade", () => { + const state = resolveGatewayDisconnectState( + "gateway closed (1008): scope upgrade pending approval", + ); + expect(state.activityStatus).toBe("device approval needed: preview latest request"); + expect(state.pairingHint).toContain("openclaw devices approve --latest"); + expect(state.pairingHint).toContain("openclaw devices approve "); }); it("falls back to idle for generic disconnect reasons", () => { diff --git a/src/tui/tui.ts b/src/tui/tui.ts index 0effaa3507ff..369636c5db17 100644 --- a/src/tui/tui.ts +++ b/src/tui/tui.ts @@ -230,12 +230,15 @@ export function resolveGatewayDisconnectState(reason?: string): { pairingHint?: string; } { const reasonLabel = reason?.trim() ? reason.trim() : "closed"; - if (/pairing required/i.test(reasonLabel)) { + // Covers both "pairing required" and a pending "scope upgrade" for a paired device. + if (/pairing required|scope upgrade/i.test(reasonLabel)) { return { connectionStatus: `gateway disconnected: ${reasonLabel}`, - activityStatus: "pairing required: run openclaw devices list", + activityStatus: "device approval needed: preview latest request", pairingHint: - "Pairing required. Run `openclaw devices list`, approve your request ID, then reconnect.", + "Device approval needed. Run `openclaw devices approve --latest` to preview the pending request, " + + "then rerun the printed `openclaw devices approve ` command " + + "(reuse `--token` or other auth flags if needed), then reconnect.", }; } return {