@@ -88,6 +88,10 @@ class SessionListViewModel(private val container: AppContainer) : ViewModel() {
8888
8989 private var previewJob: Job? = null
9090
91+ /** Per-session in-flight preview loads, so rapid SSE events for the same session
92+ * coalesce into a single request instead of firing one per event. */
93+ private val livePreviewJobs = mutableMapOf<String, Job>()
94+
9195 private val activeProfileId: String?
9296 get() = container.activeConnection.value?.profile?.id
9397
@@ -115,6 +119,8 @@ class SessionListViewModel(private val container: AppContainer) : ViewModel() {
115119 is SessionDeleted -> {
116120 val id = event.properties.info?.id ?: event.properties.sessionID
117121 if (id != null) {
122+ livePreviewJobs.remove(id)?.cancel()
123+ container.draftStore.remove(id)
118124 _state.update { s ->
119125 s.copy(
120126 sessions = s.sessions.filterNot { it.id == id },
@@ -167,17 +173,26 @@ class SessionListViewModel(private val container: AppContainer) : ViewModel() {
167173 }
168174 }
169175
170− /** Reload the preview for a single session (used by the live SSE handler). */
176+ /**
177+ * Reload the preview for a single session (used by the live SSE handler).
178+ * Cancels any prior in-flight load for the same session so a burst of
179+ * [SessionUpdated] events coalesces into a single request.
180+ */
171181 private fun loadPreview(sessionId: String) {
172182 val conn = container.activeConnection.value ?: return
173183 val api = conn.api
174− viewModelScope.launch {
175− val preview = runCatching {
176− api.listMessages(sessionId).lastOrNull()?.let { msg ->
177− msg.parts.filterIsInstance<TextPart>().lastOrNull()?.text
178− }
179− }.getOrNull()?.takeIf { it.isNotBlank() }?.take(200) ?: return@launch
180− _state.update { s -> s.copy(previews = s.previews + (sessionId to preview)) }
184+ livePreviewJobs.remove(sessionId)?.cancel()
185+ livePreviewJobs[sessionId] = viewModelScope.launch {
186+ try {
187+ val preview = runCatching {
188+ api.listMessages(sessionId).lastOrNull()?.let { msg ->
189+ msg.parts.filterIsInstance<TextPart>().lastOrNull()?.text
190+ }
191+ }.getOrNull()?.takeIf { it.isNotBlank() }?.take(200) ?: return@launch
192+ _state.update { s -> s.copy(previews = s.previews + (sessionId to preview)) }
193+ } finally {
194+ livePreviewJobs.remove(sessionId)
195+ }
181196 }
182197 }
183198
@@ -224,7 +239,10 @@ class SessionListViewModel(private val container: AppContainer) : ViewModel() {
224239 val conn = container.activeConnection.value ?: return
225240 viewModelScope.launch {
226241 runCatching { conn.repository.deleteSession(session.id) }
227− .onSuccess { refresh() }
242+ .onSuccess {
243+ container.draftStore.remove(session.id)
244+ refresh()
245+ }
228246 .onFailure {
229247 _state.value = _state.value.copy(error = null)
230248 _transientError.value = container.friendlyError(it)