Create Chat Task
Create a new SDK-driven task and kick off its first turn.
Single endpoint does three things atomically from the caller's perspective:
- Verifies the body's agent_id matches the agent bound to the presented API key. Mismatch -> 404 agent_not_found (404 not 403, so the existence of unrelated agents isn't leaked via error code).
- Persists a new :class:Task row owned by the agent's user, with source='sdk' and input set to the user message. Also persists the first user message to task_chat_messages so the existing background execution path can consume it without special-casing this entry point.
- Schedules background execution via start_task_in_background (which uses the same coroutine the WebSocket handler does). Returns 202 immediately -- callers poll GET /v1/chat/tasks/{task_id} to observe the eventual completed / failed status.
Args: request: Validated :class:CreateTaskRequest. message.content is guaranteed non-empty by Pydantic; agent_id is the target agent the SDK caller wants to invoke. authed: (Agent, AgentApiKey) tuple resolved by the auth dependency. The agent here is the key-bound agent, the single source of truth for what this caller may touch. db: SQLAlchemy session.
Returns: :class:CreateTaskResponse with the new task_id, agent_id, status='running' (the atomic claim inside the handler flips the row from PENDING to RUNNING before the response is sent), and created_at for the caller to start polling from.
Raises: V1ApiError 401: missing/invalid/revoked key (raised inside get_agent_from_api_key; envelope is uniform with other auth failures). V1ApiError 404: request.agent_id != authed_agent.id. 500 (V1 envelope): any unexpected exception -- the global handler in web/app.py translates to {"error": {"code": "internal_error", ...}} and the raw exception message stays out of the response.
Request body
Response
Successful Response