"""A2A task endpoint — the single HTTP entry the orchestrator calls per action. Collects the messages a Session pushes and returns them; the orchestrator forwards each to the frontend WebSocket. (Same proven adapter as the rest of the mesh.) """ import logging from uuid import uuid4 from fastapi import APIRouter, Depends from pydantic import BaseModel, Field from app.a2a.auth import verify_a2a_key from app.agent.session import MatchmakingAgentSession logger = logging.getLogger(__name__) router = APIRouter() class TaskRequest(BaseModel): task_id: str = Field(default_factory=lambda: str(uuid4())) action: str = "" params: dict = Field(default_factory=dict) user_id: str = "" user_context: dict | None = None session_start: bool = False class TaskCollector: """WebSocket-shaped sink that captures pushed messages into a list.""" def __init__(self): self.messages: list[dict] = [] async def accept(self): pass async def send_json(self, data: dict): self.messages.append(data) @router.post("/a2a/tasks") async def handle_a2a_task(req: TaskRequest, _token: str = Depends(verify_a2a_key)): collector = TaskCollector() session = MatchmakingAgentSession(websocket=collector, user_id=req.user_id) params = {**req.params} if req.user_context: params["user_context"] = req.user_context try: if req.session_start: await session.on_session_start(params) elif req.action: await session.on_user_action(req.action, params) else: return {"task_id": req.task_id, "status": "error", "messages": [], "error": "No action or session_start"} return {"task_id": req.task_id, "status": "completed", "messages": collector.messages} except Exception as e: # noqa: BLE001 logger.error("A2A task %s failed: %s", req.task_id, e, exc_info=True) return {"task_id": req.task_id, "status": "error", "messages": collector.messages, "error": str(e)}