"""Shared agent surface. ``BaseAgent`` is the structural contract every backend-specific agent honours. Subclasses add **capabilities as fields** (``ClaudeAgent.cwd``, ``RaycastAgent.streaming``) - backends dispatch on type, not on a runtime matrix. """ from __future__ import annotations from dataclasses import dataclass from pydantic import BaseModel, ConfigDict @dataclass(frozen=True, slots=True) class ExposedMcp: """Reference to an ``McpServer`` (by name) exposed to a single agent. ``tools`` allowlists tool names (``None`` = all); ``deny`` removes ``fnmatch`` patterns on top, e.g. ``("delete_*",)``. """ name: str tools: tuple[str, ...] | None = None deny: tuple[str, ...] = () class BaseAgent(BaseModel): """Common fields. Concrete backends subclass and add capabilities.""" model_config = ConfigDict(frozen=True, arbitrary_types_allowed=True) name: str model: str system_prompt: str expose_mcps: tuple[ExposedMcp, ...] = () accept_client_tools: bool = False @property def thinking_is_progress(self) -> bool: """Whether non-empty thinking is text for the human rather than reasoning.""" return False