feat(relay): silence mobile notifications while the desktop is focused - #8637
feat(relay): silence mobile notifications while the desktop is focused#8637ahalekelly wants to merge 7 commits into
Conversation
…used State is always published so mobile cards and Live Activities stay current. The notify field tells the relay whether to alert, and is false while a desktop client is visible and focused, derived from the client-activity leases already reported to BackgroundPolicy.
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Comment |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Want fixes drafted automatically? Bugbot Autofix can create code changes for findings. A team admin can enable Autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 7f8c2f8. Configure here.
ApprovabilityVerdict: Not approved Macroscope's review found this PR not approvable — This PR introduces a cross-service, user-visible policy that automatically changes mobile push and Live Activity alert behavior based on desktop focus and host state. It is backward compatible at the protocol level and well tested, but the production blast radius and automatic notification gate warrant human review. You can add or adjust custom eligibility rules. Learn more. |
Servers built before the field existed keep publishing to the relay; their updates alert as they always did. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
c765b8f to
749b285
Compare
Remove the hasFocusedClient branch enumeration, the schema-default test duplicated by the signature compat test, and the notify pass-through assertions in AgentActivityPublisher. Share the waiting fixture in ApnsDeliveries. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Build the notification only when notify is true, so silent publishes do not parse preferences for a payload they discard, and the single notify gate covers both the end alert and the companion push. Add a test that a silent end never enqueues the companion push. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Snapshot leases are already filtered by isLeaseActive at the snapshot's own instant, so the re-check and its now parameter could never change the result. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Compatibility issues found by the bots are fixed, it passed claude /code-review as well |

🤖 Generated by Claude Code
What Changed
Agent-activity publishes carry a signed
notifyflag, separating "update the phone's state" from "alert the user".RelayAgentActivityPublishRequestand the signed proof payload gainnotify: boolean, decoding totruewhen omitted.notifyis false while any client is visible and focused and the host is awake and unlocked, from the client-activity leases and host power stateBackgroundPolicyalready tracks. Focus is global: a user watching the app gets no phone alerts for any thread.notifyagainst the signed proof and, when false, sends Live Activity updates with no alert and skips push notifications entirely.A state published silently is not re-alerted when focus is later lost; the user saw it on screen.
Compatibility
notify; both decode totrueand match, so publishes verify and alert as before (tested in contracts and relay).Mobile is unaffected in every combination: the flag only travels server → relay, and a silent update is a Live Activity update with
alert: null, which the app already handles.Why
Every published
completed/failed/waiting_*state becomes a push. When T3 Code is focused on desktop the user is already watching, so the phone buzzing is noise — but the phone's state still has to converge, so the server can't just skip the publish. Without a channel split the alternatives are hold-and-replay (bursts late pushes for work the user already saw; #4747, +847 lines) or dropping publishes (stale cards, lost tombstones).Related: #780, #4747.
UI Changes
None.
Checklist
Note
Medium Risk
Changes signed publish verification and APNs delivery semantics for agent awareness; mismatched or tampered
notifyrejects publishes, but legacy omittingnotifystill defaults to alerting.Overview
Agent-activity publishes now carry a signed
notifyflag so the relay can refresh mobile cards and Live Activities without ringing the phone when the user is already watching on desktop.The local server still publishes every state change, but
AgentAwarenessRelaysetsnotifyto false whenBackgroundPolicy.hasFocusedClientsees a visible, focused client and the host is awake and unlocked (not suspended or locked). That value is bound into the JWT proof and HTTP payload.RelayAgentActivityPublishRequest/ proof schemas addnotifywith decode defaulttruewhen omitted for older publishers.On the relay, signature verification requires proof and request
notifyto match.AgentActivityPublisherandApnsDeliverieshonornotify: falseby skipping notification-only push fallbacks and sending Live Activity updates/ends withalert: null; no companion push is queued on end. Updates published silently while focused are not re-alerted when focus is lost later.Reviewed by Cursor Bugbot for commit ba72b28. Bugbot is set up for automated code reviews on this repo. Configure here.
Note
Silence mobile push notifications while a desktop client is focused
hasFocusedClient(snapshot)toBackgroundPolicyto detect a visible, focused client while the host is awake and unlockedAgentAwarenessRelay.makecomputesnotify = !hasFocusedClient(...)and includes it in the signed publish proof and relay payloadAgentActivityPublisherandApnsDeliverieshonornotify=falseby suppressing push notification alerts while still updating Live Activitiesnotifyboolean toRelayAgentActivityPublishProofPayloadandRelayAgentActivityPublishRequestschemas, defaulting totruefor legacy decodesEnvironmentPublishSignatures.verifyrejects requests where the signed proofnotifydoes not match the requestnotifynotifydefault totrue(no behavior change), but any mismatch between proof and requestnotifyis now rejected — verify callers in Api.ts always forward the same valueMacroscope summarized ba72b28.