agent-zero/extensions/python/user_message_ui/_10_update_check.py
Alessandro d1827e6c66
Some checks are pending
Build And Publish Docker Images / plan (push) Waiting to run
Build And Publish Docker Images / build (push) Blocked by required conditions
Refactor: use user locale for time displays
Add user-configurable timezone and 12/24-hour preferences, then wire them through settings, runtime snapshots, scheduler payloads, wait handling, notifications, backups, memory, plugin metadata, and frontend formatters.

Keep UTC as the boundary for absolute instants while serializing user-facing dates in the configured or browser-resolved timezone. Preserve scheduler wall-clock inputs in the selected timezone, propagate TZ into desktop/runtime process environments, and restart active desktop sessions when the runtime timezone changes.

Cover the risky paths with timezone regression tests for settings normalization, auto and fixed timezone resolution, scheduler round-trips, memory timestamp conversion, and desktop timezone sync.
2026-05-21 15:26:00 +02:00

111 lines
4.1 KiB
Python

from helpers import notification
from helpers.extension import Extension
from agent import LoopData
from helpers import files, settings, update_check
from helpers.localization import Localization
import datetime
import json
# check for newer versions of A0 available and send notification
# check after user message is sent from UI, not API, MCP etc. (user is active and can see the notification)
# do not check too often, use cooldown
# do not notify too often
last_check = datetime.datetime.fromtimestamp(0, tz=Localization.get().get_tzinfo())
check_cooldown_seconds = 60
last_notification_id = None
last_notification_time = datetime.datetime.fromtimestamp(0, tz=Localization.get().get_tzinfo())
notification_cooldown_seconds = 60 * 60 * 24
notification_state_file = "usr/update-check-state.json"
def _now() -> datetime.datetime:
return Localization.get().now()
def _load_notification_state() -> dict:
try:
return json.loads(files.read_file(notification_state_file))
except Exception:
return {}
def _parse_timestamp(value: str | None) -> datetime.datetime | None:
if not value:
return None
try:
parsed = datetime.datetime.fromisoformat(value)
except ValueError:
return None
if parsed.tzinfo:
return parsed.astimezone(Localization.get().get_tzinfo())
return Localization.get().localize_naive_datetime(parsed)
def _remember_notification(notif: dict, now: datetime.datetime):
state = {
"last_notification_at": now.isoformat(),
"last_notification_id": notif.get("id") or "",
"last_notification_group": notif.get("group", "update_check"),
}
files.write_file(notification_state_file, json.dumps(state, indent=2))
class UpdateCheck(Extension):
async def execute(self, loop_data: LoopData = LoopData(), text: str = "", **kwargs):
if not self.agent:
return
try:
global last_check, last_notification_id, last_notification_time
# first check if update check is enabled
current_settings = settings.get_settings()
if not current_settings["update_check_enabled"]:
return
# check if cooldown has passed
now = _now()
if (now - last_check).total_seconds() < check_cooldown_seconds:
return
last_check = now
# check for updates
version = await update_check.check_version()
# if the user should update, send notification
if notif := version.get("notification"):
now = _now()
stored_state = _load_notification_state()
stored_notification_time = _parse_timestamp(stored_state.get("last_notification_at"))
effective_notification_time = stored_notification_time or last_notification_time
if (now - effective_notification_time).total_seconds() > notification_cooldown_seconds:
last_notification_id = notif.get("id")
last_notification_time = now
try:
_remember_notification(notif, now)
except Exception:
pass
self.send_notification(notif)
except Exception as e:
pass # no need to log if the update server is inaccessible
def send_notification(self, notif):
if not self.agent:
return
notifs = self.agent.context.get_notification_manager()
notifs.send_notification(
title=notif.get("title", "Newer version available"),
message=notif.get("message", "A newer version of Agent Zero is available. Please update to the latest version."),
type=notif.get("type", "info"),
detail=notif.get("detail", ""),
display_time=notif.get("display_time", 10),
group=notif.get("group", "update_check"),
priority=notif.get("priority", notification.NotificationPriority.NORMAL),
id=notif.get("id", "update_check_available"),
)