"""Shared bounded connector contracts used by all first-party connectors.""" from __future__ import annotations from datetime import UTC, datetime from typing import Any from uuid import UUID from pydantic import BaseModel, ConfigDict, Field class ConnectorContext(BaseModel): """Validated invocation scope supplied by the Hub, never by target content.""" model_config = ConfigDict(extra="forbid", frozen=True) workspace_id: UUID project_id: UUID project_slug: str = Field(pattern=r"^[a-z0-9][a-z0-9-]{0,62}$") environment: str = Field(pattern=r"^[a-z0-9][a-z0-9-]{0,31}$") client_id: UUID | None = None correlation_id: str | None = Field(default=None, pattern=r"^[A-Za-z0-9._:-]{1,128}$") class PageRequest(BaseModel): model_config = ConfigDict(extra="forbid", frozen=True) limit: int = Field(default=20, ge=1, le=100) cursor: str | None = Field(default=None, max_length=500) class ResultMeta(BaseModel): model_config = ConfigDict(extra="forbid", frozen=True) project: str environment: str source: str truncated: bool = False next_cursor: str | None = None generated_at: datetime = Field(default_factory=lambda: datetime.now(UTC)) class ResultEnvelope[T](BaseModel): model_config = ConfigDict(extra="forbid", frozen=True) data: T meta: ResultMeta warnings: tuple[str, ...] = () class HealthSnapshot(BaseModel): model_config = ConfigDict(extra="forbid", frozen=True) status: str = Field(pattern=r"^(healthy|ready|degraded|failed)$") component: str = Field(min_length=1, max_length=100) details: dict[str, Any] = Field(default_factory=dict) class ToolPublication(BaseModel): model_config = ConfigDict(extra="forbid", frozen=True) canonical_name: str = Field(pattern=r"^[a-z0-9]+(?:\.[a-z0-9-]+){2,}$") version: str = Field(pattern=r"^\d+\.\d+\.\d+$") connector_type: str = Field(min_length=1, max_length=64) read_only: bool = True risk_class: str = Field(pattern=r"^R[0-9]+$") input_schema: dict[str, Any] output_schema: dict[str, Any] required_scopes: tuple[str, ...] = () class ResourcePublication(BaseModel): model_config = ConfigDict(extra="forbid", frozen=True) uri_template: str = Field(min_length=1, max_length=2048) connector_type: str = Field(min_length=1, max_length=64) risk_class: str = Field(pattern=r"^R[0-9]+$") mime_types: tuple[str, ...] = () max_bytes: int = Field(ge=1, le=4 * 1024 * 1024) required_scopes: tuple[str, ...] = ()