From 1bc17eb1e0096d3df89e7c78c6f85d74e763ec10 Mon Sep 17 00:00:00 2001 From: AI Supervisor Date: Sun, 8 Feb 2026 01:38:21 -0800 Subject: [PATCH] fix: Show scheduled tasks in sidebar after container restart After a container restart, scheduled tasks would disappear from the sidebar UI even though they still exist in tmp/scheduler/tasks.json. The poll endpoint only returned tasks with active AgentContext objects in memory. When the container restarts, all AgentContext objects are lost, but tasks persist in the scheduler's JSON file. This fix extends poll.py to also include tasks from the scheduler that don't have active contexts, ensuring scheduled tasks remain visible in the UI after restarts. Tested by: 1. Creating scheduled tasks 2. Restarting container with `docker restart agent-zero` 3. Verifying tasks still appear in sidebar --- python/api/poll.py | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/python/api/poll.py b/python/api/poll.py index dbe7105c66..d68a8698c4 100644 --- a/python/api/poll.py +++ b/python/api/poll.py @@ -5,6 +5,7 @@ from python.helpers.task_scheduler import TaskScheduler from python.helpers.localization import Localization from python.helpers.dotenv import get_dotenv_value +from datetime import datetime class Poll(ApiHandler): @@ -47,6 +48,7 @@ async def process(self, input: dict, request: Request) -> dict | Response: ctxs = [] tasks = [] processed_contexts = set() # Track processed context IDs + processed_task_uuids = set() # Track processed task UUIDs all_ctxs = list(AgentContext._contexts.values()) # First, identify all tasks @@ -99,10 +101,51 @@ async def process(self, input: dict, request: Request) -> dict | Response: context_data["token"] = task_details.get("token") tasks.append(context_data) + processed_task_uuids.add(task_details.get("uuid")) # Mark as processed processed_contexts.add(ctx.id) + # Also include tasks from the scheduler that don't have active AgentContexts + # This ensures tasks persist in the UI after container restarts + all_scheduler_tasks = scheduler.serialize_all_tasks() + for task_details in all_scheduler_tasks: + task_uuid = task_details.get("uuid") + if task_uuid and task_uuid not in processed_task_uuids: + # Create a minimal context-like data structure for the task + # This allows the UI to display the task even without an active context + task_data = { + "id": task_details.get("context_id", task_uuid), + "name": task_details.get("name", ""), + "created_at": task_details.get("created_at", datetime.now().isoformat()), + "type": "task", # Mark as task type for UI + "last_message": task_details.get("updated_at", task_details.get("created_at", datetime.now().isoformat())), + "task_name": task_details.get("name"), + "uuid": task_uuid, + "state": task_details.get("state"), + "system_prompt": task_details.get("system_prompt"), + "prompt": task_details.get("prompt"), + "last_run": task_details.get("last_run"), + "last_result": task_details.get("last_result"), + "attachments": task_details.get("attachments", []), + "context_id": task_details.get("context_id"), + "project": task_details.get("project"), + } + + # Add type-specific fields + task_type = task_details.get("type") + if task_type: + task_data["type"] = task_type + if task_type == "scheduled": + task_data["schedule"] = task_details.get("schedule") + elif task_type == "planned": + task_data["plan"] = task_details.get("plan") + else: + task_data["token"] = task_details.get("token") + + tasks.append(task_data) + processed_task_uuids.add(task_uuid) + # Sort tasks and chats by their creation date, descending ctxs.sort(key=lambda x: x["created_at"], reverse=True) tasks.sort(key=lambda x: x["created_at"], reverse=True)