Source code for swarph_mesh.mesh_types

"""Typed dataclasses for mesh-gateway responses.

Shape derived from the live gateway at http://lab-ovh:8788 (2026-05-08).
Stable enough to type against; Phase 3+ migrations on the gateway side
will add fields rather than rename existing ones (per the swarph
conventions captured in CLAUDE.md hedge-fund-mcp).
"""

from __future__ import annotations

from typing import Any, Optional

from pydantic import BaseModel, Field


[docs] class MeshPeer(BaseModel): """One row from ``GET /peers``.""" name: str = Field(..., description="Canonical peer name per swarph_shared registry.") url: Optional[str] = Field(None, description="Tailnet URL of the peer's claude-service.") capabilities: dict[str, Any] = Field( default_factory=dict, description="Advert payload from peer registration: can_claim_tasks, runtime, " "session_ephemeral, etc.", ) enabled: bool = Field(True, description="Gateway-side gating flag.") last_health: Optional[str] = Field(None, description="ISO timestamp of last health probe.") last_seen: Optional[str] = Field(None, description="ISO timestamp of last DM activity.") registered_at: Optional[str] = Field(None) # Forward-compat: gateway may add fields. Accept silently rather # than rejecting — adding mandatory fields here would break old # clients against new gateways. model_config = {"extra": "allow"}
[docs] class MeshMessage(BaseModel): """One row from ``GET /messages`` (or the success response of ``POST /messages``). v0.5.1 fix (drop DM #722 + #728): ``content`` is now ``Optional[str] = None``. The gateway's ``POST /messages`` success response returns ``{id, from_node, to_node, kind, thread_id, created_at}`` — no content field. Pre-fix, ``MeshClient.send`` raised ``pydantic.ValidationError`` on every successful POST despite the message landing cleanly, trapping callers wrapping ``send()`` in try/except into thinking the send failed and retrying — a DM duplication risk. The content-required model was correct for ``GET /messages`` rows (where content always present) but wrong for POST responses; making it optional fits both shapes. """ id: int from_node: str to_node: str kind: str = Field(..., description='"fyi" | "question" | "answer" | "status" | "unblock"') content: Optional[str] = Field( None, description="Message body. Required on GET /messages rows; absent in " "POST /messages success responses.", ) created_at: str = Field(..., description="ISO timestamp of mesh-gateway insert.") read_at: Optional[str] = Field(None, description="ISO timestamp when first marked read; None = unread.") related_task_id: Optional[str] = Field(None) thread_id: Optional[str] = Field(None) model_config = {"extra": "allow"}