feat(scheduler): add scheduled jobs with channel-aware delivery

Introduce persisted cron/interval jobs, gateway scheduler loop, worker turns,
and admin CRUD/edit UI. Route proactive reminders via the originating chat
channel (WhatsApp vs WeChat), harden Weixin outbound with durable queue and
PG-compatible polling, and fix chat UI to show each scheduled reminder separately.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
oliver 2026-06-26 15:56:10 +08:00
parent 5336f8835f
commit 47813f6a94
45 changed files with 4956 additions and 43 deletions

View file

@ -5,6 +5,7 @@ from typing import Any
from runtime.skill_installer import install_skill_from_registry_archive
from runtime.tools.skills.clawhub_client import get_skill_detail as clawhub_get_skill_detail
from runtime.tools.skills.clawhub_client import search_skills as clawhub_search_skills
from runtime.scheduler.cron_service import CronService
def build_common_gateway_context(*, store: Any) -> dict[str, Any]:
@ -80,6 +81,7 @@ def build_common_gateway_context(*, store: Any) -> dict[str, Any]:
return {
"store": store,
"cron": CronService(store=store),
"search_clawhub_skills": _search_clawhub_skills,
"fetch_clawhub_skill_detail": _fetch_clawhub_skill_detail,
"install_skill_from_clawhub": _install_skill_from_clawhub,

View file

@ -4,6 +4,7 @@ from interfaces.gateway.server_methods.agent import agent_handlers
from interfaces.gateway.server_methods.chat import chat_handlers
from interfaces.gateway.server_methods.commands import commands_handlers
from interfaces.gateway.server_methods.connect import connect_handlers
from interfaces.gateway.server_methods.cron import cron_handlers
from interfaces.gateway.server_methods.sessions import sessions_handlers
from interfaces.gateway.server_methods.skills import skills_handlers
from interfaces.gateway.server_methods.shared_types import GatewayRequestHandlers
@ -19,6 +20,7 @@ def build_gateway_method_handlers() -> GatewayRequestHandlers:
sessions_handlers,
skills_handlers,
agent_handlers,
cron_handlers,
):
for method, handler in (group or {}).items():
merged[str(method)] = handler

View file

@ -70,6 +70,8 @@ def _cron_list_handler(opts: dict[str, Any]) -> None:
"enabled": params.get("enabled"),
"sortBy": params.get("sortBy"),
"sortDir": params.get("sortDir"),
"tenantId": params.get("tenantId"),
"tenant_id": params.get("tenant_id"),
}
),
)