@openchambery/web 1.19.5-beta.8 → 1.19.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/dist/assets/{AgentAvatar-ojjyVOTA.js → AgentAvatar-CecaXP3U.js} +1 -1
  2. package/dist/assets/{AssistantView-Z3w0qidH.js → AssistantView-EVdG6eL4.js} +1 -1
  3. package/dist/assets/{ChatContainer-f0iiI77k.js → ChatContainer-CI9R1YEw.js} +39 -39
  4. package/dist/assets/ContextPanel-BKypMSdw.js +285 -0
  5. package/dist/assets/{DiagramView-Bft1-Ixf.js → DiagramView-Cv8NeQZw.js} +1 -1
  6. package/dist/assets/{FadeInOnReveal-Cy3sf2M5.js → FadeInOnReveal-Cj9ZC3Ab.js} +1 -1
  7. package/dist/assets/{Icon-BLWVqBee.js → Icon-Du1LX11Q.js} +1 -1
  8. package/dist/assets/MarkdownRendererImpl-BoNGeBaU.js +7 -0
  9. package/dist/assets/{MobileDetailNavigation-Ba7z2Yuy.js → MobileDetailNavigation-D_A3VHWc.js} +1 -1
  10. package/dist/assets/{MobileShareBridge-Be2_HO6w.js → MobileShareBridge-BhrdIMbw.js} +2 -2
  11. package/dist/assets/MobileSurface-CW5N2v0z.js +2 -0
  12. package/dist/assets/{MultiRunWindow-koO06fZJ.js → MultiRunWindow-Bh_4iSsl.js} +1 -1
  13. package/dist/assets/{OnboardingScreen-DepjqS6L.js → OnboardingScreen-BkbprNa7.js} +1 -1
  14. package/dist/assets/{OverlayScrollbar-GQgJusKL.js → OverlayScrollbar-oOLMqq2s.js} +1 -1
  15. package/dist/assets/{SettingsView-Cbx02C3a.js → SettingsView-DjI_CkV3.js} +8 -8
  16. package/dist/assets/{SettingsWindow-PLcPq5ST.js → SettingsWindow-DD0gPVO2.js} +1 -1
  17. package/dist/assets/{TerminalView-Bbnxr6Yi.js → TerminalView-BdWZzQZR.js} +1 -1
  18. package/dist/assets/{ToolOutputDialog-DHIdKwti.js → ToolOutputDialog-cAhekX_M.js} +1 -1
  19. package/dist/assets/appThemeRegistry-Z7AwGiu9.js +2 -0
  20. package/dist/assets/{button-CeCkEpvn.js → button-p6vbd3r6.js} +1 -1
  21. package/dist/assets/{clipboard-Dau-Ml0S.js → clipboard-C0bgPFn5.js} +1 -1
  22. package/dist/assets/{collapsible-B-aagADh.js → collapsible-Cimoz0kx.js} +1 -1
  23. package/dist/assets/{desktopHosts-BrrYPqKq.js → desktopHosts-DceeVBky.js} +1 -1
  24. package/dist/assets/{desktopRelayRestore-DR60DATT.js → desktopRelayRestore-B0MRdR5y.js} +1 -1
  25. package/dist/assets/{diagram-Cxma_IKI.js → diagram-DX_MPRs3.js} +1 -1
  26. package/dist/assets/{dialog-BsoksSPq.js → dialog-Bjnvo5pk.js} +1 -1
  27. package/dist/assets/{es-Bj9HwM-0.js → es-D9KcV5TX.js} +1 -1
  28. package/dist/assets/{fileQueries-BJKLdYos.js → fileQueries-BDld6h3B.js} +1 -1
  29. package/dist/assets/{fr-BgEHZGug.js → fr-DPA9qUl3.js} +1 -1
  30. package/dist/assets/{gitApi-C6JSZhRm.js → gitApi-9I-wy4Pz.js} +3 -3
  31. package/dist/assets/{input-CyG05w-V.js → input-gaFVNq1P.js} +1 -1
  32. package/dist/assets/insertionBoundaries-Dy_9x0TV.js +4 -0
  33. package/dist/assets/{ja-clyxf6re.js → ja-Dsif3NoD.js} +1 -1
  34. package/dist/assets/{ko-BZb3gnuq.js → ko-Dixx4Ctg.js} +1 -1
  35. package/dist/assets/{main-BNl9icid.js → main-Dy2r9c5q.js} +2 -2
  36. package/dist/assets/main-ssS4AQpG.js +29 -0
  37. package/dist/assets/{markdown-worker-C7TuaOUy.js → markdown-worker-CoO82KYF.js} +4 -4
  38. package/dist/assets/{markdownTheme-H7_7lbuh.js → markdownTheme-St8zxDvJ.js} +1 -1
  39. package/dist/assets/{miniChat-D3Vj4bki.js → miniChat-CQqw310g.js} +2 -2
  40. package/dist/assets/mobile-ClMMhPzX.js +2 -0
  41. package/dist/assets/multirun-3dsNL_2z.js +4 -0
  42. package/dist/assets/{persistence-DE9RnVcA.js → persistence-CLUawIlm.js} +1 -1
  43. package/dist/assets/{pl-CwZzSvrm.js → pl-Bj78ZGgf.js} +1 -1
  44. package/dist/assets/{platform-MjT8iqKB.js → platform-BrncW9ab.js} +1 -1
  45. package/dist/assets/projectMeta-CZLzky9G.js +3 -0
  46. package/dist/assets/{pt-BR-6ali2BQX.js → pt-BR-CFL3xSAf.js} +1 -1
  47. package/dist/assets/radio-D44bfX2h.js +1 -0
  48. package/dist/assets/renderElectronMiniChatApp-Douqsp9n.js +2 -0
  49. package/dist/assets/renderMobileApp-ScH1FVE6.js +25 -0
  50. package/dist/assets/{runtimeConfig-QnEB1ojv.js → runtimeConfig-D2zUK5uB.js} +1 -1
  51. package/dist/assets/runtimeEndpointReset-CFWN_nYN.js +4 -0
  52. package/dist/assets/{shikiHighlight-BB3Jr5bN.js → shikiHighlight-CRK1juTA.js} +1 -1
  53. package/dist/assets/{sortable-tabs-strip-DZO7_VTc.js → sortable-tabs-strip-oDpTDVoQ.js} +1 -1
  54. package/dist/assets/src-2eFAiBLq.css +2 -0
  55. package/dist/assets/{store-BvrCRq6U.js → store-DzpbXWX1.js} +2 -2
  56. package/dist/assets/{terminalApi-Bnx0KvaV.js → terminalApi-D2OzR0I3.js} +1 -1
  57. package/dist/assets/{tooltip-B__x-HDJ.js → tooltip-D1a6rKLA.js} +1 -1
  58. package/dist/assets/{uk-qRqJs4GS.js → uk-BY60Ub3Z.js} +1 -1
  59. package/dist/assets/{url-CTuw7Rjf.js → url-zwuNq3GS.js} +1 -1
  60. package/dist/assets/{useAppFontEffects-BYAihjof.js → useAppFontEffects-BIaLn7X0.js} +4 -4
  61. package/dist/assets/{useAssistantUIStore-D2K7ksGj.js → useAssistantUIStore-D699Sbdb.js} +1 -1
  62. package/dist/assets/{useDesktopWindowControlsLayout-BmORROEN.js → useDesktopWindowControlsLayout-B9oOTdt_.js} +1 -1
  63. package/dist/assets/{useEffectiveDirectory-D3iFMWXN.js → useEffectiveDirectory-BuzzKlL1.js} +1 -1
  64. package/dist/assets/{useFontPreferences-CWZnNRjz.js → useFontPreferences-F2QPNE2V.js} +1 -1
  65. package/dist/assets/{useMobileNavigationStore-CA6zYa4p.js → useMobileNavigationStore-SLsstI7D.js} +9 -9
  66. package/dist/assets/{useSessionAutoCleanup-CtycUt11.js → useSessionAutoCleanup-C3QX4RaU.js} +1 -1
  67. package/dist/assets/{useUIStore-DOxLVeGb.js → useUIStore-BXjMQrel.js} +1 -1
  68. package/dist/assets/{useWorkerHighlightedLines--45j18dY.js → useWorkerHighlightedLines-Ct_wpd4P.js} +1 -1
  69. package/dist/assets/{utils-C2R0YoC5.js → utils-D94hxQYq.js} +1 -1
  70. package/dist/assets/{zh-CN-CwZ0adcY.js → zh-CN-Cox1LaOW.js} +2 -2
  71. package/dist/assets/{zh-TW-DC0jiCzA.js → zh-TW-B6RU0mxa.js} +2 -2
  72. package/dist/index.html +9 -9
  73. package/dist/mini-chat.html +9 -9
  74. package/dist/mobile.html +9 -9
  75. package/package.json +1 -1
  76. package/server/index.js +0 -3
  77. package/server/lib/notifications/DOCUMENTATION.md +2 -2
  78. package/server/lib/notifications/push-runtime.js +4 -14
  79. package/server/lib/notifications/runtime.js +11 -20
  80. package/server/lib/notifications/runtime.test.js +4 -6
  81. package/dist/assets/ContextPanel-BsYlbdoS.js +0 -285
  82. package/dist/assets/MarkdownRendererImpl-CEfp1NzP.js +0 -7
  83. package/dist/assets/MobileSurface-B3T7Jjao.js +0 -2
  84. package/dist/assets/appThemeRegistry-SZqCMH4S.js +0 -2
  85. package/dist/assets/insertionBoundaries-rKeC9-eK.js +0 -4
  86. package/dist/assets/main-iCRPhhnY.js +0 -29
  87. package/dist/assets/mobile-6mt3nA3g.js +0 -2
  88. package/dist/assets/multirun-B0nIqgF4.js +0 -4
  89. package/dist/assets/projectMeta-CRGKGPNc.js +0 -3
  90. package/dist/assets/radio-RSR1M3tE.js +0 -1
  91. package/dist/assets/renderElectronMiniChatApp-DxvEP5C4.js +0 -2
  92. package/dist/assets/renderMobileApp-Dl3lyk4g.js +0 -23
  93. package/dist/assets/runtimeEndpointReset-D1JL05wn.js +0 -4
  94. package/dist/assets/src-CD7-ovJ5.css +0 -2
package/dist/index.html CHANGED
@@ -552,25 +552,25 @@
552
552
  opacity: 0.72;
553
553
  }
554
554
  </style>
555
- <script type="module" crossorigin src="/assets/main-BNl9icid.js"></script>
555
+ <script type="module" crossorigin src="/assets/main-Dy2r9c5q.js"></script>
556
556
  <link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-C0FnF6B9.js">
557
557
  <link rel="modulepreload" crossorigin href="/assets/vendor-base-ui-CnHfUmDN.js">
558
558
  <link rel="modulepreload" crossorigin href="/assets/vendor-zustand-B4dlGEM2.js">
559
- <link rel="modulepreload" crossorigin href="/assets/useUIStore-DOxLVeGb.js">
559
+ <link rel="modulepreload" crossorigin href="/assets/useUIStore-BXjMQrel.js">
560
560
  <link rel="modulepreload" crossorigin href="/assets/vendor-pathe-vBmvQE8f.js">
561
561
  <link rel="modulepreload" crossorigin href="/assets/vendor-react-DLPElpoQ.js">
562
562
  <link rel="modulepreload" crossorigin href="/assets/vendor-tanstack-query-core-BWz0ose6.js">
563
- <link rel="modulepreload" crossorigin href="/assets/persistence-DE9RnVcA.js">
564
- <link rel="modulepreload" crossorigin href="/assets/desktopHosts-BrrYPqKq.js">
565
- <link rel="modulepreload" crossorigin href="/assets/desktopRelayRestore-DR60DATT.js">
563
+ <link rel="modulepreload" crossorigin href="/assets/persistence-CLUawIlm.js">
564
+ <link rel="modulepreload" crossorigin href="/assets/desktopHosts-DceeVBky.js">
565
+ <link rel="modulepreload" crossorigin href="/assets/desktopRelayRestore-B0MRdR5y.js">
566
566
  <link rel="modulepreload" crossorigin href="/assets/vendor-opencode-sdk-C07HQypa.js">
567
567
  <link rel="modulepreload" crossorigin href="/assets/vendor-aparajita-capacitor-secure-storage-CjQdwfjS.js">
568
- <link rel="modulepreload" crossorigin href="/assets/platform-MjT8iqKB.js">
569
- <link rel="modulepreload" crossorigin href="/assets/terminalApi-Bnx0KvaV.js">
568
+ <link rel="modulepreload" crossorigin href="/assets/platform-BrncW9ab.js">
569
+ <link rel="modulepreload" crossorigin href="/assets/terminalApi-D2OzR0I3.js">
570
570
  <link rel="modulepreload" crossorigin href="/assets/types-BuiqMmh_.js">
571
571
  <link rel="modulepreload" crossorigin href="/assets/capgoAdapter-DCowZ2ab.js">
572
- <link rel="modulepreload" crossorigin href="/assets/runtimeConfig-QnEB1ojv.js">
573
- <link rel="stylesheet" crossorigin href="/assets/src-CD7-ovJ5.css">
572
+ <link rel="modulepreload" crossorigin href="/assets/runtimeConfig-D2zUK5uB.js">
573
+ <link rel="stylesheet" crossorigin href="/assets/src-2eFAiBLq.css">
574
574
  </head>
575
575
  <body class="h-full bg-background text-foreground">
576
576
  <!-- Keep the startup layer outside #root. React replaces every child of
@@ -74,25 +74,25 @@
74
74
  }
75
75
  </style>
76
76
 
77
- <script type="module" crossorigin src="/assets/miniChat-D3Vj4bki.js"></script>
77
+ <script type="module" crossorigin src="/assets/miniChat-CQqw310g.js"></script>
78
78
  <link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-C0FnF6B9.js">
79
79
  <link rel="modulepreload" crossorigin href="/assets/vendor-base-ui-CnHfUmDN.js">
80
80
  <link rel="modulepreload" crossorigin href="/assets/vendor-zustand-B4dlGEM2.js">
81
- <link rel="modulepreload" crossorigin href="/assets/useUIStore-DOxLVeGb.js">
81
+ <link rel="modulepreload" crossorigin href="/assets/useUIStore-BXjMQrel.js">
82
82
  <link rel="modulepreload" crossorigin href="/assets/vendor-pathe-vBmvQE8f.js">
83
83
  <link rel="modulepreload" crossorigin href="/assets/vendor-react-DLPElpoQ.js">
84
84
  <link rel="modulepreload" crossorigin href="/assets/vendor-tanstack-query-core-BWz0ose6.js">
85
- <link rel="modulepreload" crossorigin href="/assets/persistence-DE9RnVcA.js">
86
- <link rel="modulepreload" crossorigin href="/assets/desktopHosts-BrrYPqKq.js">
87
- <link rel="modulepreload" crossorigin href="/assets/desktopRelayRestore-DR60DATT.js">
85
+ <link rel="modulepreload" crossorigin href="/assets/persistence-CLUawIlm.js">
86
+ <link rel="modulepreload" crossorigin href="/assets/desktopHosts-DceeVBky.js">
87
+ <link rel="modulepreload" crossorigin href="/assets/desktopRelayRestore-B0MRdR5y.js">
88
88
  <link rel="modulepreload" crossorigin href="/assets/vendor-opencode-sdk-C07HQypa.js">
89
89
  <link rel="modulepreload" crossorigin href="/assets/vendor-aparajita-capacitor-secure-storage-CjQdwfjS.js">
90
- <link rel="modulepreload" crossorigin href="/assets/platform-MjT8iqKB.js">
91
- <link rel="modulepreload" crossorigin href="/assets/terminalApi-Bnx0KvaV.js">
90
+ <link rel="modulepreload" crossorigin href="/assets/platform-BrncW9ab.js">
91
+ <link rel="modulepreload" crossorigin href="/assets/terminalApi-D2OzR0I3.js">
92
92
  <link rel="modulepreload" crossorigin href="/assets/types-BuiqMmh_.js">
93
93
  <link rel="modulepreload" crossorigin href="/assets/capgoAdapter-DCowZ2ab.js">
94
- <link rel="modulepreload" crossorigin href="/assets/runtimeConfig-QnEB1ojv.js">
95
- <link rel="stylesheet" crossorigin href="/assets/src-CD7-ovJ5.css">
94
+ <link rel="modulepreload" crossorigin href="/assets/runtimeConfig-D2zUK5uB.js">
95
+ <link rel="stylesheet" crossorigin href="/assets/src-2eFAiBLq.css">
96
96
  </head>
97
97
  <body class="h-full bg-background text-foreground">
98
98
  <div id="root" class="h-full">
package/dist/mobile.html CHANGED
@@ -63,25 +63,25 @@
63
63
  })();
64
64
  </script>
65
65
 
66
- <script type="module" crossorigin src="/assets/mobile-6mt3nA3g.js"></script>
66
+ <script type="module" crossorigin src="/assets/mobile-ClMMhPzX.js"></script>
67
67
  <link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-C0FnF6B9.js">
68
68
  <link rel="modulepreload" crossorigin href="/assets/vendor-base-ui-CnHfUmDN.js">
69
69
  <link rel="modulepreload" crossorigin href="/assets/vendor-zustand-B4dlGEM2.js">
70
- <link rel="modulepreload" crossorigin href="/assets/useUIStore-DOxLVeGb.js">
70
+ <link rel="modulepreload" crossorigin href="/assets/useUIStore-BXjMQrel.js">
71
71
  <link rel="modulepreload" crossorigin href="/assets/vendor-pathe-vBmvQE8f.js">
72
72
  <link rel="modulepreload" crossorigin href="/assets/vendor-react-DLPElpoQ.js">
73
73
  <link rel="modulepreload" crossorigin href="/assets/vendor-tanstack-query-core-BWz0ose6.js">
74
- <link rel="modulepreload" crossorigin href="/assets/persistence-DE9RnVcA.js">
75
- <link rel="modulepreload" crossorigin href="/assets/desktopHosts-BrrYPqKq.js">
76
- <link rel="modulepreload" crossorigin href="/assets/desktopRelayRestore-DR60DATT.js">
74
+ <link rel="modulepreload" crossorigin href="/assets/persistence-CLUawIlm.js">
75
+ <link rel="modulepreload" crossorigin href="/assets/desktopHosts-DceeVBky.js">
76
+ <link rel="modulepreload" crossorigin href="/assets/desktopRelayRestore-B0MRdR5y.js">
77
77
  <link rel="modulepreload" crossorigin href="/assets/vendor-opencode-sdk-C07HQypa.js">
78
78
  <link rel="modulepreload" crossorigin href="/assets/vendor-aparajita-capacitor-secure-storage-CjQdwfjS.js">
79
- <link rel="modulepreload" crossorigin href="/assets/platform-MjT8iqKB.js">
80
- <link rel="modulepreload" crossorigin href="/assets/terminalApi-Bnx0KvaV.js">
79
+ <link rel="modulepreload" crossorigin href="/assets/platform-BrncW9ab.js">
80
+ <link rel="modulepreload" crossorigin href="/assets/terminalApi-D2OzR0I3.js">
81
81
  <link rel="modulepreload" crossorigin href="/assets/types-BuiqMmh_.js">
82
82
  <link rel="modulepreload" crossorigin href="/assets/capgoAdapter-DCowZ2ab.js">
83
- <link rel="modulepreload" crossorigin href="/assets/runtimeConfig-QnEB1ojv.js">
84
- <link rel="stylesheet" crossorigin href="/assets/src-CD7-ovJ5.css">
83
+ <link rel="modulepreload" crossorigin href="/assets/runtimeConfig-D2zUK5uB.js">
84
+ <link rel="stylesheet" crossorigin href="/assets/src-2eFAiBLq.css">
85
85
  </head>
86
86
  <body class="h-full bg-background text-foreground">
87
87
  <div id="root" class="h-full"></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openchambery/web",
3
- "version": "1.19.5-beta.8",
3
+ "version": "1.19.5",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "./server/index.js",
package/server/index.js CHANGED
@@ -342,8 +342,6 @@ const updateUiVisibility = (token, visible, platform) => {
342
342
  if (visible === true) clearPendingPushBadge();
343
343
  return pushRuntime.updateUiVisibility(token, visible, platform);
344
344
  };
345
- const isAnyUiVisible = (...args) => pushRuntime.isAnyUiVisible(...args);
346
- const isAnyInteractiveClientVisible = (...args) => pushRuntime.isAnyInteractiveClientVisible(...args);
347
345
  const isUiVisible = (...args) => pushRuntime.isUiVisible(...args);
348
346
  const ensurePushInitialized = (...args) => pushRuntime.ensurePushInitialized(...args);
349
347
  const setPushInitialized = (...args) => pushRuntime.setPushInitialized(...args);
@@ -664,7 +662,6 @@ const notificationTriggerRuntime = createNotificationTriggerRuntime({
664
662
  sendPushToAllUiSessions,
665
663
  sendApnsToAllUiSessions,
666
664
  sendLiveActivityEnd,
667
- isAnyInteractiveClientVisible,
668
665
  buildOpenCodeUrl,
669
666
  getOpenCodeAuthHeaders,
670
667
  });
@@ -52,7 +52,7 @@ This module provides notification message preparation utilities for the web serv
52
52
  - session meta cache for child-session and small-model suppression
53
53
  - template resolution and fallback behavior
54
54
  - native notification fanout and web push payload fanout
55
- - push suppression while any fresh UI visibility heartbeat reports a focused client
55
+ - push always fans out to every subscribed surface; another client's visibility never suppresses delivery
56
56
 
57
57
  ### Push runtime API (push-runtime.js)
58
58
  - `createPushRuntime(dependencies)`: creates runtime for web push and UI visibility state.
@@ -62,7 +62,7 @@ This module provides notification message preparation utilities for the web serv
62
62
  - `setPushInitialized(value)`
63
63
  - `addOrUpdatePushSubscription(uiSessionToken, subscription, userAgent)`
64
64
  - `removePushSubscription(uiSessionToken, endpoint)`
65
- - `sendPushToAllUiSessions(payload, options?)`
65
+ - `sendPushToAllUiSessions(payload)`
66
66
  - `updateUiVisibility(token, visible)`
67
67
  - `isAnyUiVisible()`
68
68
  - `isUiVisible(token)`
@@ -221,8 +221,7 @@ export const createPushRuntime = (deps) => {
221
221
  }
222
222
  };
223
223
 
224
- const sendPushToAllUiSessions = async (payload, options = {}) => {
225
- const requireNoSse = options.requireNoSse === true;
224
+ const sendPushToAllUiSessions = async (payload) => {
226
225
  const store = await readPushSubscriptionsFromDisk();
227
226
  const sessions = store.subscriptionsBySession || {};
228
227
  const subscriptionsByEndpoint = new Map();
@@ -239,14 +238,6 @@ export const createPushRuntime = (deps) => {
239
238
  }
240
239
 
241
240
  await Promise.all(Array.from(subscriptionsByEndpoint.values()).map(async (sub) => {
242
- if (requireNoSse) {
243
- // Mobile PWA subscriptions follow the same presence model as native push: suppress only
244
- // when an interactive (desktop/web) client is visible. The phone PWA's own foreground is
245
- // handled in the service worker (focused-client check), so it won't double-notify.
246
- // Non-mobile (desktop/web) subscriptions keep the existing any-visible gate.
247
- const suppressed = isMobilePlatform(sub.platform) ? isAnyInteractiveClientVisible() : isAnyUiVisible();
248
- if (suppressed) return;
249
- }
250
241
  await sendPushToSubscription(sub, payload);
251
242
  }));
252
243
  };
@@ -278,10 +269,9 @@ export const createPushRuntime = (deps) => {
278
269
  return false;
279
270
  };
280
271
 
281
- // True when at least one NON-mobile client (desktop/web/vscode) is currently visible. Used to
282
- // suppress native push to the phone: an active desktop already shows the notification, so the
283
- // phone doesn't need it. Deliberately based on the desktop's visibility (reliable), never the
284
- // phone's own (a backgrounded WKWebView can't report "hidden" before iOS suspends it).
272
+ // True when at least one NON-mobile client (desktop/web/vscode) is currently visible.
273
+ // Not a push gate delivery always fans out. Kept as a visibility query for tests and
274
+ // diagnostics. Deliberately based on the desktop's visibility, never the phone's own.
285
275
  const isAnyInteractiveClientVisible = () => {
286
276
  const now = Date.now();
287
277
  pruneUiVisibility(now);
@@ -12,7 +12,6 @@ export const createNotificationTriggerRuntime = (deps) => {
12
12
  sendPushToAllUiSessions,
13
13
  sendApnsToAllUiSessions,
14
14
  sendLiveActivityEnd,
15
- isAnyInteractiveClientVisible,
16
15
  buildOpenCodeUrl,
17
16
  getOpenCodeAuthHeaders,
18
17
  } = deps;
@@ -68,25 +67,17 @@ export const createNotificationTriggerRuntime = (deps) => {
68
67
  };
69
68
 
70
69
  // Fan a notification out to every delivery channel: browser web-push (full templated
71
- // payload) and native iOS APNs (generic model-based text). Both share the dedup tag and
72
- // `requireNoSse` focus gate; a failure in one channel must not block the other.
73
- const fanoutPush = (payload, options) => {
74
- // Presence-aware routing: if any interactive (non-mobile) client — desktop/web/vscode — is
75
- // currently visible, it already shows the in-app notification, so skip the native push to the
76
- // phone. Gated on the desktop's visibility (reliable), never the phone's own. When we skip we
77
- // also skip toApnsGenericPayload, so the badge isn't incremented for an undelivered push.
78
- const interactiveVisible = isAnyInteractiveClientVisible?.() === true;
79
- return Promise.all([
80
- Promise.resolve(sendPushToAllUiSessions?.(payload, options)).catch((error) => {
81
- console.warn('[Push] web-push fanout failed:', error?.message ?? error);
82
- }),
83
- interactiveVisible
84
- ? Promise.resolve()
85
- : Promise.resolve(sendApnsToAllUiSessions?.(toApnsGenericPayload(payload), options)).catch((error) => {
86
- console.warn('[APNs] fanout failed:', error?.message ?? error);
87
- }),
88
- ]);
89
- };
70
+ // payload) and native iOS APNs (generic model-based text). Both share the dedup tag;
71
+ // a failure in one channel must not block the other. Visibility on any other client
72
+ // never suppresses delivery every subscribed surface gets the push.
73
+ const fanoutPush = (payload, options) => Promise.all([
74
+ Promise.resolve(sendPushToAllUiSessions?.(payload, options)).catch((error) => {
75
+ console.warn('[Push] web-push fanout failed:', error?.message ?? error);
76
+ }),
77
+ Promise.resolve(sendApnsToAllUiSessions?.(toApnsGenericPayload(payload), options)).catch((error) => {
78
+ console.warn('[APNs] fanout failed:', error?.message ?? error);
79
+ }),
80
+ ]);
90
81
 
91
82
  let getIsWindowFocused = typeof deps.getIsWindowFocused === 'function'
92
83
  ? deps.getIsWindowFocused
@@ -28,7 +28,6 @@ const createRuntime = (overrides = {}) => {
28
28
  sendPushToAllUiSessions,
29
29
  sendApnsToAllUiSessions,
30
30
  sendLiveActivityEnd,
31
- isAnyInteractiveClientVisible: () => false,
32
31
  buildOpenCodeUrl: (path) => `http://opencode.test${path}`,
33
32
  getOpenCodeAuthHeaders: () => ({}),
34
33
  getIsWindowFocused: () => false,
@@ -224,14 +223,13 @@ describe('notification trigger live activity end', () => {
224
223
  expect(sendApnsToAllUiSessions).not.toHaveBeenCalled();
225
224
  });
226
225
 
227
- it('ends the live activity even when an interactive client is visible', async () => {
226
+ it('still sends native push when another client is visible', async () => {
228
227
  vi.stubGlobal('fetch', vi.fn(async () => rootSessionResponse()));
229
- const { runtime, sendLiveActivityEnd, sendApnsToAllUiSessions } = createRuntime({
230
- isAnyInteractiveClientVisible: () => true,
231
- });
228
+ const { runtime, sendLiveActivityEnd, sendApnsToAllUiSessions, sendPushToAllUiSessions } = createRuntime();
232
229
  await runtime.maybeSendPushForTrigger(completionPayload());
233
230
  expect(sendLiveActivityEnd).toHaveBeenCalledWith({ sessionId: 'ses_root', status: 'complete' });
234
- expect(sendApnsToAllUiSessions).not.toHaveBeenCalled();
231
+ expect(sendPushToAllUiSessions).toHaveBeenCalledTimes(1);
232
+ expect(sendApnsToAllUiSessions).toHaveBeenCalledTimes(1);
235
233
  });
236
234
 
237
235
  it('suppresses live activity end for child and small-model sessions', async () => {