feat(core,frontends,agents): agent kinds, frontend routing, anthropic on conversations, vault mirror

This commit is contained in:
hh
2026-08-28 03:50:33 +02:00
parent 33ccc78fec
commit 1d8d65b69a
17 changed files with 1009 additions and 292 deletions
+22 -1
View File
@@ -22,6 +22,7 @@ if TYPE_CHECKING:
from beaver_gateway.core.registry import AgentRegistry, McpRegistry
from beaver_gateway.core.turn_record import TurnRecord
from beaver_gateway.storage import Database
from beaver_gateway.storage.models import Conversation, ConversationBinding
TurnLogHandler = Callable[[TurnRecord], Awaitable[None]]
@@ -86,10 +87,30 @@ class GatewayRuntime:
class Frontend(ABC):
"""Listens on a port, dispatches into the gateway."""
"""Listens on a port, dispatches into the gateway.
A frontend that shows conversations declares ``name`` (the binding
key) and ``kinds`` (which conversation kinds it shows);
``core/conversations`` refuses to bind a conversation to a frontend
outside its declaration. The first frontend in declaration order
whose ``materialize`` returns a binding is the *home* of that kind:
``spawn`` calls it so a new conversation gets a window (a vault file,
a Telegram topic). ``agent_for`` names the default agent for a kind
so callers may omit ``agent``. Stateless frontends (MCP, admin) keep
the defaults and stay outside the routing.
"""
name: str = ""
kinds: tuple[str, ...] = ()
@abstractmethod
def configure(self, runtime: GatewayRuntime) -> None: ...
@abstractmethod
async def serve(self) -> None: ...
def agent_for(self, kind: str) -> str | None: # noqa: ARG002
return None
async def materialize(self, conv: Conversation) -> ConversationBinding | None: # noqa: ARG002
return None