mirror of
https://github.com/stablyai/orca.git
synced 2026-11-14 01:47:24 +00:00
fix(notifications): stop clearing phone alerts that were never sent (#37095)
Reading an agent on the desktop rebuilt notification ids for turns that never alerted (every chat pane, silenced workers) and forwarded each one to the phone as a silent dismiss push plus a dismissal-store record. Only forward a clear when the store holds a live delivery for that id.
This commit is contained in:
@@ -141,6 +141,36 @@ describe('notifications:dismiss by acknowledged subject', () => {
|
||||
expect(dismissMobileNotification).toHaveBeenCalledWith('agent:phone-only')
|
||||
})
|
||||
|
||||
// Reads rebuild ids for turns that never alerted; each forwarded one was a silent push and a store slot.
|
||||
it('clears on the phone only alerts that reached it, including ones sent before a restart', async () => {
|
||||
const directory = mkdtempSync(join(tmpdir(), 'orca-clear-delivered-'))
|
||||
const original = new RuntimeMobileNotificationController()
|
||||
const restarted = new RuntimeMobileNotificationController()
|
||||
try {
|
||||
original.configureDismissalStore(directory)
|
||||
original.dispatch({
|
||||
type: 'notification',
|
||||
source: 'agent-task-complete',
|
||||
title: 'Done',
|
||||
body: '',
|
||||
notificationId: 'agent:sent'
|
||||
})
|
||||
await original.closeDismissals()
|
||||
restarted.configureDismissalStore(directory)
|
||||
const events: MobileNotificationEvent[] = []
|
||||
restarted.onDispatched((event) => events.push(event))
|
||||
register({ suppressWhenFocused: false }, restarted)
|
||||
getDismissHandler()({}, ['agent:never-sent', 'agent:sent'], [PANE])
|
||||
getDismissHandler()({}, ['agent:sent'], [PANE])
|
||||
expect(events.map((event) => [event.type, event.notificationId])).toEqual([
|
||||
['dismiss', 'agent:sent']
|
||||
])
|
||||
} finally {
|
||||
await restarted.closeDismissals()
|
||||
rmSync(directory, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
|
||||
it('records nothing for a request neither the desktop nor the phone announced', async () => {
|
||||
const { dismissMobileNotification } = register({ suppressWhenFocused: false })
|
||||
const dispatch = getDispatchHandler()
|
||||
|
||||
@@ -121,15 +121,19 @@ export class MobileNotificationDismissalStore {
|
||||
return this.flush()
|
||||
}
|
||||
|
||||
/** Unreadable history cannot rule a delivery out. */
|
||||
hasLiveDelivery(notificationId: string): boolean {
|
||||
const now = Date.now()
|
||||
return (
|
||||
this.unreadable ||
|
||||
this.entries.some((entry) => entry.notificationId === notificationId && isLive(entry, now))
|
||||
)
|
||||
}
|
||||
|
||||
liveDeliveries(prefix = ''): DeliveredNotificationRecord[] {
|
||||
const now = Date.now()
|
||||
return this.entries
|
||||
.filter(
|
||||
(entry) =>
|
||||
entry.expiresAt > now &&
|
||||
entry.notificationId.startsWith(prefix) &&
|
||||
entry.dismissedThrough < entry.notificationSeq
|
||||
)
|
||||
.filter((entry) => entry.notificationId.startsWith(prefix) && isLive(entry, now))
|
||||
.map(({ notificationId, notificationEpoch, notificationSeq, structuredOrigin }) => ({
|
||||
notificationId,
|
||||
notificationEpoch,
|
||||
@@ -153,6 +157,10 @@ export class MobileNotificationDismissalStore {
|
||||
}
|
||||
}
|
||||
|
||||
function isLive(entry: RecordEntry, now: number): boolean {
|
||||
return entry.expiresAt > now && entry.dismissedThrough < entry.notificationSeq
|
||||
}
|
||||
|
||||
/** Keep records with unknown future origin kinds, degrading only their origin to none. */
|
||||
function readEntry(value: unknown): RecordEntry[] {
|
||||
if (!isEntry(value)) {
|
||||
|
||||
@@ -188,6 +188,10 @@ export class RuntimeMobileNotificationController {
|
||||
}
|
||||
|
||||
dismiss(notificationId: string): void {
|
||||
// Reads rebuild ids for alerts that never went out (chat panes, silenced workers); each would cost a silent push.
|
||||
if (this.dismissalStore && !this.dismissalStore.hasLiveDelivery(notificationId)) {
|
||||
return
|
||||
}
|
||||
this.dispatch({ type: 'dismiss', notificationId })
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user