From 273550b28b51a15dc27680dcfcbd74dfd5ca6ecd Mon Sep 17 00:00:00 2001 From: chenjintao Date: Wed, 12 Aug 2026 10:30:47 +0800 Subject: [PATCH] fix(multimodal): reconnect websocket with fresh auth --- .../multimodal/control_plane_multimodal_runtime.c | 4 ++++ .../control_plane_multimodal_session_service.c | 5 +++++ .../src/runtime/control_plane_runtime_workers.c | 7 ++++++- .../transport_ws/transport_ws_workers_impl.c | 4 ++-- tools/check_architecture_guards.sh | 14 ++++++++++++++ 5 files changed, 31 insertions(+), 3 deletions(-) diff --git a/components/control_plane/src/multimodal/control_plane_multimodal_runtime.c b/components/control_plane/src/multimodal/control_plane_multimodal_runtime.c index 724a9fc..7bf46da 100644 --- a/components/control_plane/src/multimodal/control_plane_multimodal_runtime.c +++ b/components/control_plane/src/multimodal/control_plane_multimodal_runtime.c @@ -547,11 +547,15 @@ static void control_plane_multimodal_event_bridge(control_plane_multimodal_event case CONTROL_PLANE_MULTIMODAL_EVENT_TRANSPORT_DISCONNECTED: control_plane_clear_cloud_chouqian_pending("transport disconnected"); control_plane_multimodal_stop_uplink(); + ESP_LOGI(TAG, "Schedule fresh-auth reconnect after transport disconnect"); + (void)control_plane_schedule_websocket_wait_task(); break; case CONTROL_PLANE_MULTIMODAL_EVENT_TRANSPORT_ERROR: control_plane_clear_cloud_chouqian_pending("transport error"); control_plane_multimodal_stop_uplink(); + ESP_LOGI(TAG, "Schedule fresh-auth reconnect after transport error"); + (void)control_plane_schedule_websocket_wait_task(); break; case CONTROL_PLANE_MULTIMODAL_EVENT_TASK_FAILED: diff --git a/components/control_plane/src/multimodal/control_plane_multimodal_session_service.c b/components/control_plane/src/multimodal/control_plane_multimodal_session_service.c index 1fb37b3..83bda77 100644 --- a/components/control_plane/src/multimodal/control_plane_multimodal_session_service.c +++ b/components/control_plane/src/multimodal/control_plane_multimodal_session_service.c @@ -462,10 +462,15 @@ void control_plane_multimodal_mark_transport_unavailable( control_plane_multimodal_stop_uplink(); bool close_transport = reason == CONTROL_PLANE_TRANSPORT_LOSS_PROTOCOL_ERROR || + reason == CONTROL_PLANE_TRANSPORT_LOSS_WS_ERROR || + reason == CONTROL_PLANE_TRANSPORT_LOSS_WS_DISCONNECTED || reason == CONTROL_PLANE_TRANSPORT_LOSS_SEND_TEXT_FAILURE || reason == CONTROL_PLANE_TRANSPORT_LOSS_LISTENING_TIMEOUT || reason == CONTROL_PLANE_TRANSPORT_LOSS_NO_RESPONSE_TIMEOUT; if (close_transport) { + ESP_LOGI(TAG, + "Retire unavailable transport: reason=%s; next start requires fresh auth", + transport_loss_reason_name(reason)); esp_err_t stop_err = domain_runtime_multimodal_transport_stop(0); if (stop_err != ESP_OK) { ESP_LOGW(TAG, diff --git a/components/control_plane/src/runtime/control_plane_runtime_workers.c b/components/control_plane/src/runtime/control_plane_runtime_workers.c index 1118878..81a7eb5 100644 --- a/components/control_plane/src/runtime/control_plane_runtime_workers.c +++ b/components/control_plane/src/runtime/control_plane_runtime_workers.c @@ -1547,7 +1547,12 @@ static void control_plane_wifi_state_task(void *arg) if (ws_start_due && !control_plane_get_ptt_active() && !control_plane_multimodal_is_connected()) { - control_plane_try_start_multimodal_from_state_machine(&last_ws_start_attempt_tick, false); + bool ws_started = control_plane_try_start_multimodal_from_state_machine( + &last_ws_start_attempt_tick, + false); + if (!ws_started) { + (void)control_plane_schedule_websocket_wait_task(); + } now_tick = xTaskGetTickCount(); } control_plane_drive_ptt_state_machine(&last_ws_start_attempt_tick); diff --git a/components/platform/src/multimodal/transport_ws/transport_ws_workers_impl.c b/components/platform/src/multimodal/transport_ws/transport_ws_workers_impl.c index 4eb98f2..d2d833a 100644 --- a/components/platform/src/multimodal/transport_ws/transport_ws_workers_impl.c +++ b/components/platform/src/multimodal/transport_ws/transport_ws_workers_impl.c @@ -754,8 +754,8 @@ static esp_err_t start_websocket_client_with_fallback(void) .headers = s_headers, .network_timeout_ms = CONFIG_MULTIMODAL_CONNECT_TIMEOUT_MS, .reconnect_timeout_ms = CONFIG_MULTIMODAL_RECONNECT_DELAY_MS, - .disable_auto_reconnect = false, - .enable_close_reconnect = true, + .disable_auto_reconnect = true, + .enable_close_reconnect = false, .task_stack = candidates[i], .task_prio = CONFIG_MULTIMODAL_WEBSOCKET_TASK_PRIORITY, .task_name = "multimodal_ws", diff --git a/tools/check_architecture_guards.sh b/tools/check_architecture_guards.sh index 5e3b436..7ba9c5f 100644 --- a/tools/check_architecture_guards.sh +++ b/tools/check_architecture_guards.sh @@ -83,6 +83,20 @@ if ! rg -n '#include "esp_afe_aec.h"' components/platform/src/multimodal/audio_p ! rg -n 'afe_aec_process\(' components/platform/src/multimodal/audio_pipeline/audio_pipeline_runtime_impl.c >/dev/null 2>&1; then fail "Platform must own the synchronous ESP-SR AEC implementation" fi +if rg -n '\.disable_auto_reconnect = false|\.enable_close_reconnect = true' \ + components/platform/src/multimodal/transport_ws/transport_ws_workers_impl.c >/dev/null 2>&1 || + ! rg -n '\.disable_auto_reconnect = true' \ + components/platform/src/multimodal/transport_ws/transport_ws_workers_impl.c >/dev/null 2>&1 || + ! rg -n 'reason == CONTROL_PLANE_TRANSPORT_LOSS_WS_ERROR' \ + components/control_plane/src/multimodal/control_plane_multimodal_session_service.c >/dev/null 2>&1 || + ! rg -n 'reason == CONTROL_PLANE_TRANSPORT_LOSS_WS_DISCONNECTED' \ + components/control_plane/src/multimodal/control_plane_multimodal_session_service.c >/dev/null 2>&1 || + ! rg -n 'Schedule fresh-auth reconnect after transport (error|disconnect)' \ + components/control_plane/src/multimodal/control_plane_multimodal_runtime.c >/dev/null 2>&1 || + ! rg -n 'if \(!ws_started\)' \ + components/control_plane/src/runtime/control_plane_runtime_workers.c >/dev/null 2>&1; then + fail "WebSocket recovery must retire stale credentials and reconnect through fresh control-plane authentication" +fi if rg -n 'esp_afe_aec\.h|afe_aec_process\(' \ components/domain/{include,internal,src} \ components/control_plane/{include,internal,src} \