"""Task Center event bridge. Converts task lifecycle actions and agent execution events into normalized AIOS Task Center events. Uses duck-typed Agent Event Bus publisher when available and an in-memory fallback for tests/offline runs. """ from __future__ import annotations from dataclasses import asdict, dataclass, field from datetime import datetime, timezone from typing import Any, Protocol from uuid import uuid4 def utc_now() -> str: return datetime.now(timezone.utc).isoformat() class TaskEventPublisher(Protocol): def publish( self, kind: str, source: str, target: str, summary: str, payload: dict[str, Any], correlation_id: str | None = None, ) -> dict[str, Any]: ... @dataclass(frozen=True) class TaskEvent: id: str kind: str source: str target: str summary: str payload: dict[str, Any] correlation_id: str | None = None created_at: str = field(default_factory=utc_now) def to_dict(self) -> dict[str, Any]: return asdict(self) class InMemoryTaskEventBus: def __init__(self): self.events: list[dict[str, Any]] = [] def publish( self, kind: str, source: str, target: str, summary: str, payload: dict[str, Any], correlation_id: str | None = None, ) -> dict[str, Any]: event = TaskEvent( id=f"task_evt_{uuid4().hex[:12]}", kind=kind, source=source, target=target, summary=summary, payload=payload, correlation_id=correlation_id, ).to_dict() self.events.append(event) return event class TaskEventBridge: def __init__(self, bus: TaskEventPublisher | None = None): self.bus = bus or InMemoryTaskEventBus() def task_created(self, task: dict[str, Any]) -> dict[str, Any]: return self.bus.publish( kind="task_created", source=task.get("assigned_by", "unknown"), target=task.get("executor", "task-center"), summary=task.get("title", "task created"), payload=task, correlation_id=task.get("task_id"), ) def task_updated(self, task: dict[str, Any], action: str) -> dict[str, Any]: return self.bus.publish( kind="task_updated", source=task.get("executor", "task-center"), target="task-center", summary=f"task {action}: {task.get('status')}", payload={"action": action, "task": task}, correlation_id=task.get("task_id"), ) def task_blocked(self, task: dict[str, Any], reason: str) -> dict[str, Any]: return self.bus.publish( kind="task_blocked", source=task.get("executor", "unknown"), target="owner-gate", summary=reason, payload={"task": task, "reason": reason}, correlation_id=task.get("task_id"), ) def approval_requested(self, task: dict[str, Any], reason: str) -> dict[str, Any]: return self.bus.publish( kind="task_approval_requested", source=task.get("executor", "unknown"), target="owner-gate", summary=reason, payload={"task": task, "reason": reason}, correlation_id=task.get("task_id"), ) def notification_sent(self, task: dict[str, Any], channel: str) -> dict[str, Any]: return self.bus.publish( kind="task_notification_sent", source="notification-agent", target=task.get("assigned_by", "owner"), summary=f"notification sent via {channel}", payload={"task": task, "channel": channel}, correlation_id=task.get("task_id"), ) __all__ = ["TaskEvent", "InMemoryTaskEventBus", "TaskEventBridge"]