from datetime import datetime from uuid import UUID from pydantic import BaseModel, EmailStr, Field # ── Auth ── class RegisterRequest(BaseModel): email: EmailStr password: str = Field(min_length=8) display_name: str | None = None timezone: str = "America/Chicago" class LoginRequest(BaseModel): email: EmailStr password: str class AppleAuthRequest(BaseModel): identity_token: str authorization_code: str full_name: str | None = None class RefreshRequest(BaseModel): refresh_token: str class AuthResponse(BaseModel): access_token: str refresh_token: str expires_in: int user: "UserOut" class UserOut(BaseModel): id: UUID email: str | None display_name: str | None timezone: str created_at: datetime # ── Tasks ── class TaskCreate(BaseModel): title: str description: str | None = None priority: int = Field(0, ge=0, le=4) deadline: datetime | None = None estimated_minutes: int | None = None tags: list[str] = [] class TaskUpdate(BaseModel): title: str | None = None description: str | None = None priority: int | None = Field(None, ge=0, le=4) status: str | None = None deadline: datetime | None = None estimated_minutes: int | None = None tags: list[str] | None = None class TaskOut(BaseModel): id: UUID user_id: UUID title: str description: str | None priority: int status: str deadline: datetime | None estimated_minutes: int | None source: str tags: list[str] plan_type: str | None brain_dump_raw: str | None created_at: datetime updated_at: datetime class BrainDumpRequest(BaseModel): raw_text: str source: str = "manual" timezone: str = "America/Chicago" class ParsedSubtask(BaseModel): title: str description: str | None = None deadline: datetime | None = None estimated_minutes: int | None = None suggested: bool = False class ParsedTask(BaseModel): task_id: str | None = None title: str description: str | None = None priority: int = 0 deadline: datetime | None = None estimated_minutes: int | None = None tags: list[str] = [] subtasks: list[ParsedSubtask] = [] class BrainDumpResponse(BaseModel): parsed_tasks: list[ParsedTask] unparseable_fragments: list[str] = [] ask_for_plans: bool = True class PlanRequest(BaseModel): plan_type: str = "llm_generated" # ── Steps ── class StepOut(BaseModel): id: UUID task_id: UUID sort_order: int title: str description: str | None estimated_minutes: int | None status: str checkpoint_note: str | None last_checked_at: datetime | None completed_at: datetime | None created_at: datetime class StepUpdate(BaseModel): title: str | None = None description: str | None = None estimated_minutes: int | None = None sort_order: int | None = None status: str | None = None checkpoint_note: str | None = None class PlanResponse(BaseModel): task_id: UUID plan_type: str steps: list[StepOut] # ── Sessions ── class SessionStartRequest(BaseModel): task_id: UUID | None = None platform: str = "mac" work_app_bundle_ids: list[str] | None = None class SessionCheckpointRequest(BaseModel): current_step_id: UUID | None = None last_action_summary: str | None = None next_up: str | None = None goal: str | None = None active_app: str | None = None last_screenshot_analysis: str | None = None attention_score: int | None = None distraction_count: int | None = None class SessionEndRequest(BaseModel): status: str = "completed" class OpenSessionOut(BaseModel): id: UUID task_id: UUID | None task: dict | None status: str platform: str started_at: datetime ended_at: datetime | None checkpoint: dict class SessionJoinRequest(BaseModel): platform: str = "ipad" work_app_bundle_ids: list[str] | None = None class SessionJoinResponse(BaseModel): session_id: UUID joined: bool task: dict | None current_step: dict | None all_steps: list[StepOut] suggested_app_scheme: str | None suggested_app_name: str | None class SessionOut(BaseModel): id: UUID user_id: UUID task_id: UUID | None platform: str started_at: datetime ended_at: datetime | None status: str checkpoint: dict created_at: datetime class ResumeCard(BaseModel): welcome_back: str you_were_doing: str next_step: str motivation: str class SessionResumeResponse(BaseModel): session_id: UUID task: dict | None current_step: dict | None progress: dict resume_card: ResumeCard # ── Distractions ── class ScreenshotAnalysisResponse(BaseModel): on_task: bool current_step_id: UUID | None checkpoint_note_update: str | None steps_completed: list[UUID] = [] distraction_type: str | None app_name: str | None confidence: float gentle_nudge: str | None vlm_summary: str | None class AppActivityRequest(BaseModel): session_id: UUID app_bundle_id: str app_name: str duration_seconds: int returned_to_task: bool = False class AppActivityResponse(BaseModel): distraction_logged: bool session_distraction_count: int gentle_nudge: str | None class AppCheckRequest(BaseModel): app_bundle_id: str class AppCheckResponse(BaseModel): is_distraction_app: bool pending_task_count: int most_urgent_task: dict | None nudge: str | None # ── Analyze Result (Device-Side VLM) ── class ProposedAction(BaseModel): label: str action_type: str details: str | None = None class FrictionDetection(BaseModel): type: str = "none" confidence: float = 0.0 description: str | None = None proposed_actions: list[ProposedAction] = [] source_context: str | None = None target_context: str | None = None class SessionAction(BaseModel): type: str = "none" # resume | switch | complete | start_new | none session_id: UUID | None = None reason: str | None = None class AnalyzeResultRequest(BaseModel): session_id: UUID | None = None # optional — VLM can run without an active session on_task: bool current_step_id: UUID | None = None inferred_task: str | None = None checkpoint_note_update: str | None = None steps_completed: list[UUID] = [] friction: FrictionDetection = FrictionDetection() session_action: SessionAction = SessionAction() intent: str | None = None distraction_type: str | None = None app_name: str | None = None confidence: float = 0.0 gentle_nudge: str | None = None vlm_summary: str | None = None class AnalyzeResultResponse(BaseModel): side_effects_applied: bool steps_updated: int distraction_logged: bool proactive_action_id: UUID | None = None # ── Proactive Actions ── class ProactiveRespondRequest(BaseModel): proactive_action_id: UUID user_choice: str # accepted | declined | alternative_chosen chosen_action: str | None = None class ProactiveRespondResponse(BaseModel): logged: bool should_execute: bool class ProactiveExecuteRequest(BaseModel): proactive_action_id: UUID action_type: str execution_params: dict = {} class ProactiveExecuteResponse(BaseModel): executed: bool result: str | None = None class ProactivePreference(BaseModel): preferred_action: str | None total_choices: int acceptance_rate: float class ProactivePreferencesResponse(BaseModel): preferences: dict[str, ProactivePreference] # ── Device Token ── class DeviceTokenRequest(BaseModel): platform: str token: str # ── Analytics ── class AnalyticsSummary(BaseModel): total_focus_minutes: float sessions_completed: int tasks_completed: int top_distractors: list[dict] avg_attention_score: float | None