@@ -19,6 +19,7 @@ import soy.iko.opencode.data.repo.responseStatusCode
1919import soy.iko.opencode.notification.NotificationChannels
2020import soy.iko.opencode.notification.SessionNotifications
2121import soy.iko.opencode.R
22+import soy.iko.opencode.util.runCatchingCancellable
2223import kotlinx.coroutines.CoroutineScope
2324import kotlinx.coroutines.Dispatchers
2425import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -178,8 +179,10 @@ class AppContainer(context: Context) {
178179 // Track sessions actively streaming so we know which idle events
179180 // represent a finished run worth notifying about.
180181 if (event is MessagePartUpdated || event is MessageUpdated) {
181− if (activeRuns.size >= activeRunsLimit) activeRuns.clear()
182− activeRuns.add(sid)
182+ synchronized(activeRuns) {
183+ if (activeRuns.size >= activeRunsLimit) activeRuns.clear()
184+ activeRuns.add(sid)
185+ }
183186 }
184187 }
185188 }
@@ -203,7 +206,7 @@ class AppContainer(context: Context) {
203206 /** Resolve the title for [sessionId] (best-effort) and post a completion notification. */
204207 private suspend fun notifySessionCompleted(sessionId: String) {
205208 val conn = activeConnection.value ?: return
206− val title = runCatching {
209+ val title = runCatchingCancellable {
207210 conn.repository.listSessions().firstOrNull { it.id == sessionId }?.displayTitle
208211 }.getOrNull() ?: sessionId
209212 SessionNotifications.postCompleted(appContext, sessionId, title)
@@ -221,7 +224,7 @@ class AppContainer(context: Context) {
221224 .getOrDefault(emptyList())
222225 .firstOrNull() ?: return
223226 _reconnecting.value = true
224− val ok = runCatching {
227+ val ok = runCatchingCancellable {
225228 val conn = connect(recent)
226229 conn.api.ping()
227230 }.isSuccess