fix(ui): keep limited-access dismissal through reconnect (#130127)

* fix(ui): preserve access snooze through reconnect

Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>

* test(commands): preserve fs-safe mock exports

---------

Co-authored-by: roboclaw-bot <309084314+roboclaw-bot@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
This commit is contained in:
RoboClaw
2026-08-26 07:06:06 -07:00
committed by GitHub
parent 6f0395ec79
commit f09ec76de4
2 changed files with 22 additions and 3 deletions
+6 -2
View File
@@ -374,11 +374,15 @@ class SidebarAttention extends OpenClawLightDomElement {
if (!this.dismissedScope || !this.context) {
return;
}
const snapshot = this.context.gateway.snapshot;
const scopes = snapshot.hello?.auth?.scopes;
const entry = buildScopeUpgradeInboxEntry({
scopes: this.context.gateway.snapshot.hello?.auth?.scopes,
scopes,
state: this.context.scopeUpgrade.state,
});
if (!entry?.dismissal) {
// A disconnect makes access unresolved, not resolved. Keep the snooze until
// connected scope facts or an active request lifecycle authoritatively retire it.
if (snapshot.phase === "connected" && scopes !== undefined && !entry?.dismissal) {
this.dismissed = clearSidebarAttentionDismissal(this.dismissedScope, "scopeUpgrade");
}
}
+16 -1
View File
@@ -44,6 +44,15 @@ function requireRecord(value: unknown): Record<string, unknown> {
return value as Record<string, unknown>;
}
async function gatewayPhase(page: Page): Promise<string | undefined> {
return page.evaluate(() => {
const app = document.querySelector("openclaw-app") as HTMLElement & {
runtime?: { context: { gateway: { snapshot: { phase: string } } } };
};
return app.runtime?.context.gateway.snapshot.phase;
});
}
async function captureProof(page: Page, name: string): Promise<void> {
if (!proofDir) {
return;
@@ -134,7 +143,7 @@ describeControlUiE2e("Control UI live device scope upgrade", () => {
it("moves limited access into the Inbox and persists its dismissal", async () => {
const desktopContext = await createContext();
const desktop = await desktopContext.newPage();
await installMockGateway(desktop, { operatorScopes: LIMITED_SCOPES });
const gateway = await installMockGateway(desktop, { operatorScopes: LIMITED_SCOPES });
await desktop.goto(`${server.baseUrl}activity`);
expect(await desktop.locator(".scope-upgrade-status-trigger").count()).toBe(0);
@@ -151,6 +160,12 @@ describeControlUiE2e("Control UI live device scope upgrade", () => {
await desktopPanel.getByRole("tab", { name: "System", exact: true }).waitFor();
await captureProof(desktop, "desktop-inbox-limited-access-dismissed.png");
await gateway.setOnline(false);
await expect.poll(() => gatewayPhase(desktop)).toBe("reconnecting");
await gateway.setOnline(true);
await expect.poll(() => gatewayPhase(desktop)).toBe("connected");
await expect.poll(() => desktopInbox.getAttribute("aria-label")).toBe("0 inbox items");
await desktop.reload();
await desktop.locator("openclaw-app-shell").waitFor();
await expect.poll(() => desktopInbox.getAttribute("aria-label")).toBe("0 inbox items");