"""Typed representations of agent-permissions manifests.""" from __future__ import annotations from datetime import datetime from enum import Enum from typing import List, Optional from pydantic import BaseModel, ConfigDict, Field, HttpUrl class StandardVerb(str, Enum): ALL = "all" READ_CONTENT = "read_content" READ_METADATA = "read_metadata" FOLLOW_LINK = "follow_link" CLICK_ELEMENT = "click_element" SCROLL_PAGE = "scroll_page" SET_INPUT_VALUE = "set_input_value" SUBMIT_FORM = "submit_form" EXECUTE_SCRIPT = "execute_script" PLAY_MEDIA = "play_media" PAUSE_MEDIA = "pause_media" MUTE_MEDIA = "mute_media" UNMUTE_MEDIA = "unmute_media" UPLOAD_FILE = "upload_file" DOWNLOAD_FILE = "download_file" COPY_TO_CLIPBOARD = "copy_to_clipboard" STANDARD_VERBS = {verb.value for verb in StandardVerb} class Directive(str, Enum): MUST = "MUST" MUST_NOT = "MUST NOT" SHOULD = "SHOULD" SHOULD_NOT = "SHOULD NOT" class ManifestMetadata(BaseModel): model_config = ConfigDict(extra="forbid") schema_version: str = Field(pattern=r"^\d+\.\d+\.\d+$") last_updated: datetime author: Optional[str] = None class RateLimit(BaseModel): model_config = ConfigDict(extra="forbid") max_requests: int = Field(gt=0) window_seconds: int = Field(gt=0) class Modifiers(BaseModel): model_config = ConfigDict(extra="forbid") burst: Optional[int] = Field(default=None, gt=0) rate_limit: Optional[RateLimit] = None time_window: Optional[str] = Field( default=None, pattern=r"^[0-2]\d:[0-5]\d-[0-2]\d:[0-5]\d UTC$", ) human_in_the_loop: Optional[bool] = None class ResourceRule(BaseModel): model_config = ConfigDict(extra="forbid") verb: StandardVerb | str selector: str = Field(min_length=1) allowed: bool modifiers: Optional[Modifiers] = None class ActionGuideline(BaseModel): model_config = ConfigDict(extra="forbid") directive: Directive description: str exceptions: Optional[str] = None class APIReference(BaseModel): model_config = ConfigDict(extra="forbid") type: str = Field(pattern=r"^(openapi|mcp|a2a)$") endpoint: HttpUrl description: str docs: Optional[HttpUrl] = None class Manifest(BaseModel): model_config = ConfigDict(extra="forbid") metadata: ManifestMetadata resource_rules: List[ResourceRule] action_guidelines: List[ActionGuideline] = Field(default_factory=list) api: List[APIReference] = Field(default_factory=list) def allowed_verbs(self) -> set[str]: """Return the set of allowed verbs.""" return set(STANDARD_VERBS) def to_dict(self) -> dict: """Serialize manifest to dict using pydantic.""" return self.model_dump(mode="json")