Polish the update notification action
Present Open updater as the existing confirm-style button on its own toast row. Opening the Self Update modal now dismisses the persistent notification so users do not need to close it separately.\n\nKeep external update-instruction links unchanged and extend the persistent-toast regression coverage.
Alessandro committed
Jul 12, 2026 at 17:10 UTC
831bed7a0cc39ab8a7c19a388d06357374fe3c46
4 files changed
+23
-2
extensions/python/user_message_ui/AGENTS.md
+1
-1
@@ -11,7 +11,7 @@
11
## Local Contracts
12
13
- Keep proactive UI messages relevant, non-spammy, and safe for display.
14
-- Keep update-available notifications visible until the user dismisses them.
14
+- Keep update-available notifications visible until the user dismisses them or opens the updater.
15
- Do not expose local diagnostics or update data that should stay internal.
16
17
## Work Guidance
extensions/python/user_message_ui/_10_update_check.py
+13
-1
@@ -98,10 +98,22 @@ class UpdateCheck(Extension):
98
if not self.agent:
99
return
100
101
+ message = notif.get(
102
+ "message",
103
+ "A newer version of Agent Zero is available. Please update to the latest version.",
104
+ )
105
+ message = message.replace(
106
+ '<a href="#" @click.prevent="$store.selfUpdateStore.openModal()">Open updater</a>.',
107
+ '<div class="toast-action-row">'
108
+ '<button type="button" class="button confirm" '
109
+ '@click="$store.selfUpdateStore.openModal(); '
110
+ '$store.notificationStore.dismissToast(toast.toastId)">'
111
+ "Open updater</button></div>",
112
+ )
113
notifs = self.agent.context.get_notification_manager()
114
notifs.send_notification(
115
title=notif.get("title", "Newer version available"),
104
- message=notif.get("message", "A newer version of Agent Zero is available. Please update to the latest version."),
116
+ message=message,
117
type=notif.get("type", "info"),
118
detail=notif.get("detail", ""),
119
display_time=0,
tests/test_download_toast_regressions.py
+5
@@ -88,6 +88,7 @@ def previous_non_space(source: str, index: int) -> str:
88
89
def test_notification_store_supports_persistent_grouped_toasts():
90
store = read("webui", "components", "notifications", "notification-store.js")
91
+ toast_stack = read("webui", "components", "notifications", "notification-toast-stack.html")
92
api = read("api", "notification_create.py")
93
plugins = read("helpers", "plugins.py")
94
update_check = read("extensions", "python", "user_message_ui", "_10_update_check.py")
@@ -103,6 +104,10 @@ def test_notification_store_supports_persistent_grouped_toasts():
104
assert 'id=notif.get("id", "update_check_available"),' in update_check
105
assert "display_time=0," in plugins
106
assert "display_time=0," in update_check
107
+ assert 'class="toast-action-row"' in update_check
108
+ assert 'class="button confirm"' in update_check
109
+ assert "$store.notificationStore.dismissToast(toast.toastId)" in update_check
110
+ assert ".toast-action-row" in toast_stack
111
112
113
def test_backup_zip_downloads_emit_grouped_preparing_and_downloading_toasts():
webui/components/notifications/notification-toast-stack.html
+4
@@ -150,6 +150,10 @@
150
margin-bottom: 4px;
151
}
152
153
+ .toast-action-row {
154
+ margin-top: 10px;
155
+ }
156
+
157
.toast-timestamp {
158
font-size: 0.75rem;
159
color: var(--color-text);