@@ -65,7 +65,9 @@ class OpencodeCore(
6565 // The auth fields are showing but empty — sending the request now
6666 // would just 401 again in a loop. Prompt for credentials instead.
6767 if (authRequired && !hasCredentials()) { error = "Enter username and password"; emit(); return }
68− loading = true; error = null; authRequired = false; emit()
68+ // Leave authRequired as-is; probeHealth sets it from the response (true on
69+ // 401, false on success). Resetting it here just flickers the auth fields.
70+ loading = true; error = null; emit()
6971 scope.launch { probeHealth() }
7072 }
7173
@@ -335,15 +337,17 @@ class OpencodeCore(
335337
336338 private fun handleServerEvent(json: String) {
337339 val event = Protocol.parseEvent(json) ?: return
338− applyEvent(event)
339− emit()
340+ // Only re-render if the event actually changed our state; applyEvent returns
341+ // false for events that don't apply (other session, unknown message, …).
342+ if (applyEvent(event)) emit()
340343 }
341344
342− private fun applyEvent(event: Protocol.Event) {
345+ /** Apply a server event to the model. Returns true iff any state actually changed. */
346+ private fun applyEvent(event: Protocol.Event): Boolean {
343347 when (event) {
344348 is Protocol.Event.MessageUpdated -> {
345349 val info = event.info
346− if (info.role != "assistant" || !isCurrent(info.sessionId)) return
350+ if (info.role != "assistant" || !isCurrent(info.sessionId)) return false
347351 val streaming = info.time.completed == null && info.error == null
348352 val existing = messages.firstOrNull { it.id == info.id }
349353 if (existing != null) {
@@ -353,11 +357,12 @@ class OpencodeCore(
353357 messages.add(MsgState(info.id, "assistant", info.time.created).apply { this.streaming = streaming })
354358 }
355359 info.error?.let { error = it.message(); generating = false }
360+ return true
356361 }
357362
358363 is Protocol.Event.PartUpdated -> {
359364 val part = event.part
360− val msg = messages.firstOrNull { it.id == part.messageId } ?: return
365+ val msg = messages.firstOrNull { it.id == part.messageId } ?: return false
361366 when (part) {
362367 is Protocol.Part.Text -> if (!part.synthetic) {
363368 msg.textParts[part.id] = pickText(part.text, event.delta, msg.textParts[part.id])
@@ -367,24 +372,31 @@ class OpencodeCore(
367372 is Protocol.Part.Tool ->
368373 msg.toolParts[part.id] = ToolView(part.tool, part.status, part.title)
369374 }
375+ return true
370376 }
371377
372− is Protocol.Event.PartRemoved -> messages.firstOrNull { it.id == event.messageId }?.remove(event.partId)
378+ is Protocol.Event.PartRemoved -> {
379+ val msg = messages.firstOrNull { it.id == event.messageId } ?: return false
380+ msg.remove(event.partId)
381+ return true
382+ }
373383
374− is Protocol.Event.MessageRemoved -> messages.removeAll { it.id == event.messageId }
384+ is Protocol.Event.MessageRemoved -> return messages.removeAll { it.id == event.messageId }
375385
376− is Protocol.Event.SessionIdle -> if (isCurrent(event.sessionId)) {
386+ is Protocol.Event.SessionIdle -> {
387+ if (!isCurrent(event.sessionId)) return false
377388 generating = false
378389 messages.forEach { it.streaming = false }
390+ return true
379391 }
380392
381393 is Protocol.Event.SessionError -> {
382394 val scoped = event.sessionId == null || isCurrent(event.sessionId)
383− if (scoped) {
384− event.error?.let { error = it.message() }
385− generating = false
386− messages.forEach { it.streaming = false }
387− }
395+ if (!scoped) return false
396+ event.error?.let { error = it.message() }
397+ generating = false
398+ messages.forEach { it.streaming = false }
399+ return true
388400 }
389401
390402 is Protocol.Event.SessionUpserted -> {
@@ -396,18 +408,22 @@ class OpencodeCore(
396408 sessions.add(0, SessionView(event.info.id, title))
397409 }
398410 if (isCurrent(event.info.id)) currentSessionTitle = title
411+ return true
399412 }
400413
401− is Protocol.Event.SessionDeleted -> event.sessionId?.let { id ->
402− sessions.removeAll { it.id == id }
414+ is Protocol.Event.SessionDeleted -> {
415+ val id = event.sessionId ?: return false
416+ val removed = sessions.removeAll { it.id == id }
403417 if (isCurrent(id)) {
404418 currentSessionId = null
405419 messages.clear()
406420 screen = Screen.Sessions
421+ return true
407422 }
423+ return removed
408424 }
409425
410− Protocol.Event.Other -> Unit
426+ Protocol.Event.Other -> return false
411427 }
412428 }
413429