summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAdam <[email protected]>2026-02-11 08:47:22 -0600
committerAdam <[email protected]>2026-02-11 08:47:27 -0600
commita52fe28246f05683b7a52782eafa038c899808db (patch)
treeaff49d1ad06755e744fec9dbd700b2893baa8428
parent8c5ba8aeb0850b65d752889994c3fa327746e3e9 (diff)
downloadopencode-a52fe28246f05683b7a52782eafa038c899808db.tar.gz
opencode-a52fe28246f05683b7a52782eafa038c899808db.zip
fix(app): notifications on child sessions
-rw-r--r--packages/app/src/context/notification.tsx107
1 files changed, 61 insertions, 46 deletions
diff --git a/packages/app/src/context/notification.tsx b/packages/app/src/context/notification.tsx
index b876bd862..cade70a53 100644
--- a/packages/app/src/context/notification.tsx
+++ b/packages/app/src/context/notification.tsx
@@ -69,7 +69,7 @@ export const { use: useNotification, provider: NotificationProvider } = createSi
}),
)
- const meta = { pruned: false }
+ const meta = { pruned: false, disposed: false }
createEffect(() => {
if (!ready()) return
@@ -84,6 +84,17 @@ export const { use: useNotification, provider: NotificationProvider } = createSi
const index = createMemo(() => buildNotificationIndex(store.list))
+ const lookup = (directory: string, sessionID?: string) => {
+ if (!sessionID) return Promise.resolve(undefined)
+ const [syncStore] = globalSync.child(directory, { bootstrap: false })
+ const match = Binary.search(syncStore.session, sessionID, (s) => s.id)
+ if (match.found) return Promise.resolve(syncStore.session[match.index])
+ return globalSDK.client.session
+ .get({ directory, sessionID })
+ .then((x) => x.data)
+ .catch(() => undefined)
+ }
+
const unsub = globalSDK.event.listen((e) => {
const event = e.details
if (event.type !== "session.idle" && event.type !== "session.error") return
@@ -102,61 +113,65 @@ export const { use: useNotification, provider: NotificationProvider } = createSi
switch (event.type) {
case "session.idle": {
const sessionID = event.properties.sessionID
- const [syncStore] = globalSync.child(directory, { bootstrap: false })
- const match = Binary.search(syncStore.session, sessionID, (s) => s.id)
- const session = match.found ? syncStore.session[match.index] : undefined
- if (session?.parentID) break
-
- playSound(soundSrc(settings.sounds.agent()))
-
- append({
- directory,
- time,
- viewed: viewed(sessionID),
- type: "turn-complete",
- session: sessionID,
+ void lookup(directory, sessionID).then((session) => {
+ if (meta.disposed) return
+ if (!session) return
+ if (session.parentID) return
+
+ playSound(soundSrc(settings.sounds.agent()))
+
+ append({
+ directory,
+ time,
+ viewed: viewed(sessionID),
+ type: "turn-complete",
+ session: sessionID,
+ })
+
+ const href = `/${base64Encode(directory)}/session/${sessionID}`
+ if (settings.notifications.agent()) {
+ void platform.notify(
+ language.t("notification.session.responseReady.title"),
+ session.title ?? sessionID,
+ href,
+ )
+ }
})
-
- const href = `/${base64Encode(directory)}/session/${sessionID}`
- if (settings.notifications.agent()) {
- void platform.notify(
- language.t("notification.session.responseReady.title"),
- session?.title ?? sessionID,
- href,
- )
- }
break
}
case "session.error": {
const sessionID = event.properties.sessionID
- const [syncStore] = globalSync.child(directory, { bootstrap: false })
- const match = sessionID ? Binary.search(syncStore.session, sessionID, (s) => s.id) : undefined
- const session = sessionID && match?.found ? syncStore.session[match.index] : undefined
- if (session?.parentID) break
-
- playSound(soundSrc(settings.sounds.errors()))
-
- const error = "error" in event.properties ? event.properties.error : undefined
- append({
- directory,
- time,
- viewed: viewed(sessionID),
- type: "error",
- session: sessionID ?? "global",
- error,
+ void lookup(directory, sessionID).then((session) => {
+ if (meta.disposed) return
+ if (session?.parentID) return
+
+ playSound(soundSrc(settings.sounds.errors()))
+
+ const error = "error" in event.properties ? event.properties.error : undefined
+ append({
+ directory,
+ time,
+ viewed: viewed(sessionID),
+ type: "error",
+ session: sessionID ?? "global",
+ error,
+ })
+ const description =
+ session?.title ??
+ (typeof error === "string" ? error : language.t("notification.session.error.fallbackDescription"))
+ const href = sessionID ? `/${base64Encode(directory)}/session/${sessionID}` : `/${base64Encode(directory)}`
+ if (settings.notifications.errors()) {
+ void platform.notify(language.t("notification.session.error.title"), description, href)
+ }
})
- const description =
- session?.title ??
- (typeof error === "string" ? error : language.t("notification.session.error.fallbackDescription"))
- const href = sessionID ? `/${base64Encode(directory)}/session/${sessionID}` : `/${base64Encode(directory)}`
- if (settings.notifications.errors()) {
- void platform.notify(language.t("notification.session.error.title"), description, href)
- }
break
}
}
})
- onCleanup(unsub)
+ onCleanup(() => {
+ meta.disposed = true
+ unsub()
+ })
return {
ready,