From c76f65665c2fb449a60ba3cccf5140d62b75fd01 Mon Sep 17 00:00:00 2001 From: Shubham Naik Date: Wed, 18 Mar 2026 15:37:47 -0700 Subject: [PATCH] =?UTF-8?q?fix(listener):=20emit=20corrected=20device=20st?= =?UTF-8?q?atus=20after=20permission=20mode=20syn=E2=80=A6=20(#1437)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Letta Code --- src/websocket/listener/turn.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/websocket/listener/turn.ts b/src/websocket/listener/turn.ts index 426d1f0..c3e7cb3 100644 --- a/src/websocket/listener/turn.ts +++ b/src/websocket/listener/turn.ts @@ -48,6 +48,7 @@ import { } from "./permissionMode"; import { emitCanonicalMessageDelta, + emitDeviceStatusIfOpen, emitInterruptedStatusDelta, emitLoopErrorDelta, emitLoopStatusUpdate, @@ -769,6 +770,18 @@ export async function handleIncomingMessage( turnPermissionModeState, ); + // Emit a corrected device status now that the permission mode is synced. + // The emitRuntimeStateUpdates() calls earlier in the turn read from the map + // before setConversationPermissionModeState() ran, so they emitted a stale + // current_permission_mode. This final emission sends the correct value, + // ensuring the web UI (and desktop) always reflect mode changes from + // EnterPlanMode/ExitPlanMode and that mid-turn web permission changes + // are not reverted by a stale emission at turn end. + emitDeviceStatusIfOpen(runtime, { + agent_id: agentId || null, + conversation_id: conversationId, + }); + runtime.activeAbortController = null; runtime.cancelRequested = false; runtime.isRecoveringApprovals = false;