update checker

frdel committed Nov 17, 2025 at 11:32 UTC 269aafdc284e8d7c896e97609aff87ebfc14d2b0
5 files changed +114 -7
python/api/message.py
+7 -1
@@ -1,7 +1,7 @@
1 from agent import AgentContext, UserMessage
2 from python.helpers.api import ApiHandler, Request, Response
3
4 -from python.helpers import files
4 +from python.helpers import files, extension
5 import os
6 from werkzeug.utils import secure_filename
7 from python.helpers.defer import DeferredTask
@@ -55,6 +55,12 @@ class Message(ApiHandler):
55 # Obtain agent context
56 context = self.use_context(ctxid)
57
58 + # call extension point, alow it to modify data
59 + data = { "message": message, "attachment_paths": attachment_paths }
60 + await extension.call_extensions("user_message_ui", agent=context.get_agent(), data=data)
61 + message = data.get("message", "")
62 + attachment_paths = data.get("attachment_paths", [])
63 +
64 # Store attachments in agent data
65 # context.agent0.set_data("attachments", attachment_paths)
66
python/extensions/user_message_ui/_10_update_check.py new
+58
@@ -0,0 +1,58 @@
1 +from python.helpers import notification
2 +from python.helpers.extension import Extension
3 +from agent import LoopData
4 +from python.helpers import settings, update_check
5 +import datetime
6 +
7 +
8 +# check for newer versions of A0 available and send notification
9 +# check after user message is sent from UI, not API, MCP etc. (user is active and can see the notification)
10 +# do not check too often, use cooldown
11 +# do not notify too often unless there's a different notification
12 +
13 +last_check = datetime.datetime.fromtimestamp(0)
14 +check_cooldown_seconds = 60
15 +last_notification_id = ""
16 +last_notification_time = datetime.datetime.fromtimestamp(0)
17 +notification_cooldown_seconds = 60 * 60 * 24
18 +
19 +class UpdateCheck(Extension):
20 +
21 + async def execute(self, loop_data: LoopData = LoopData(), text: str = "", **kwargs):
22 + try:
23 + global last_check, last_notification_id, last_notification_time
24 +
25 + # first check if update check is enabled
26 + current_settings = settings.get_settings()
27 + if not current_settings["update_check_enabled"]:
28 + return
29 +
30 + # check if cooldown has passed
31 + if (datetime.datetime.now() - last_check).total_seconds() < check_cooldown_seconds:
32 + return
33 + last_check = datetime.datetime.now()
34 +
35 + # check for updates
36 + version = await update_check.check_version()
37 +
38 + # if the user should update, send notification
39 + if notif := version.get("notification"):
40 + if notif.get("id") != last_notification_id or (datetime.datetime.now() - last_notification_time).total_seconds() > notification_cooldown_seconds:
41 + last_notification_id = notif.get("id")
42 + last_notification_time = datetime.datetime.now()
43 + self.send_notification(notif)
44 + except Exception as e:
45 + pass # no need to log if the update server is inaccessible
46 +
47 +
48 + def send_notification(self, notif):
49 + notifs = self.agent.context.get_notification_manager()
50 + notifs.send_notification(
51 + title=notif.get("title", "Newer version available"),
52 + message=notif.get("message", "A newer version of Agent Zero is available. Please update to the latest version."),
53 + type=notif.get("type", "info"),
54 + detail=notif.get("detail", ""),
55 + display_time=notif.get("display_time", 10),
56 + group=notif.get("group", "update_check"),
57 + priority=notif.get("priority", notification.NotificationPriority.NORMAL),
58 + )
python/helpers/git.py
+8 -1
@@ -47,4 +47,11 @@ def get_git_info():
47 "version": version
48 }
49
50 - return git_info
\ No newline at end of file
50 + return git_info
51 +
52 +def get_version():
53 + try:
54 + git_info = get_git_info()
55 + return str(git_info.get("short_tag", "")).strip() or "unknown"
56 + except Exception:
57 + return "unknown"
\ No newline at end of file
python/helpers/settings.py
+26 -5
@@ -111,6 +111,8 @@ class Settings(TypedDict):
111 # LiteLLM global kwargs applied to all model calls
112 litellm_global_kwargs: dict[str, Any]
113
114 + update_check_enabled: bool
115 +
116 class PartialSettings(Settings, total=False):
117 pass
118
@@ -1217,6 +1219,27 @@ def convert_out(settings: Settings) -> SettingsOutput:
1219 "tab": "external",
1220 }
1221
1222 + # update checker section
1223 + update_checker_fields: list[SettingsField] = []
1224 +
1225 + update_checker_fields.append(
1226 + {
1227 + "id": "update_check_enabled",
1228 + "title": "Enable Update Checker",
1229 + "description": "Enable update checker to notify about newer versions of Agent Zero.",
1230 + "type": "switch",
1231 + "value": settings["update_check_enabled"],
1232 + }
1233 + )
1234 +
1235 + update_checker_section: SettingsSection = {
1236 + "id": "update_checker",
1237 + "title": "Update Checker",
1238 + "description": "Update checker periodically checks for new releases of Agent Zero and will notify when an update is recommended.<br>No personal data is sent to the update server, only randomized+anonymized unique ID and current version number, which help us evaluate the importance of the update in case of critical bug fixes etc.",
1239 + "fields": update_checker_fields,
1240 + "tab": "external",
1241 + }
1242 +
1243 # Backup & Restore section
1244 backup_fields: list[SettingsField] = []
1245
@@ -1269,6 +1292,7 @@ def convert_out(settings: Settings) -> SettingsOutput:
1292 mcp_server_section,
1293 a2a_section,
1294 external_api_section,
1295 + update_checker_section,
1296 backup_section,
1297 dev_section,
1298 # code_exec_section,
@@ -1502,6 +1526,7 @@ def get_default_settings() -> Settings:
1526 variables="",
1527 secrets="",
1528 litellm_global_kwargs={},
1529 + update_check_enabled=True,
1530 )
1531
1532
@@ -1706,8 +1731,4 @@ def create_auth_token() -> str:
1731
1732
1733 def _get_version():
1709 - try:
1710 - git_info = git.get_git_info()
1711 - return str(git_info.get("short_tag", "")).strip() or "unknown"
1712 - except Exception:
1713 - return "unknown"
1734 + return git.get_version()
python/helpers/update_check.py new
+15
@@ -0,0 +1,15 @@
1 +from python.helpers import git, runtime
2 +import hashlib
3 +
4 +async def check_version():
5 + import httpx
6 +
7 + current_version = git.get_version()
8 + anonymized_id = hashlib.sha256(runtime.get_persistent_id().encode()).hexdigest()[:20]
9 +
10 + url = "https://api.agent-zero.ai/a0-update-check"
11 + payload = {"current_version": current_version, "anonymized_id": anonymized_id}
12 + async with httpx.AsyncClient() as client:
13 + response = await client.post(url, json=payload)
14 + version = response.json()
15 + return version
\ No newline at end of file