feat: Pheby platform adapter/plugin for Hermes (protocol v1)
Third-party Hermes platform plugin serving the Pheby WebSocket+HTTPS protocol for the native Android client, behind Caddy: - Platform adapter (BasePlatformAdapter subclass) registered via the documented plugin system (plugins.platforms pattern, kind: platform) - Multiple conversations mapped to Hermes sessions (authoritative state) - Streaming chat via GatewayStreamConsumer edit path (message.delta) - Structured tool events (never fake tool text in chat), incl. post_tool_call hook relay with Hermes tool_call_ids - Native approval + clarification round-trips via tools.approval / tools.clarify_gateway primitives - Attachment delivery: adapter-owned copies, opaque IDs, persistent metadata, 7-day retention + safe cleanup, authenticated HTTPS download - Model + reasoning-effort query/change via Hermes picker data and session/global overrides - Shared-secret auth (constant-time, lockout), size limits, path- traversal-proof attachment resolution, minimal health endpoint - Protocol v1 spec with JSON examples (docs/PROTOCOL.md) - Install/config/Caddy/security docs + discovered Hermes limitations - 37 passing tests (auth, conversations, protocol, attachments incl. expiry/traversal, tool events, approvals, clarifications, cancel, reconnect re-sync, live WS smoke tests) — gateway-free fakes No Hermes core modifications required.
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
"""Pheby — Hermes Agent platform adapter/plugin for the Pheby Android client.
|
||||
|
||||
A third-party Hermes platform plugin. Install this package directory as
|
||||
``~/.hermes/plugins/pheby/`` (HERMES_HOME/plugins/pheby), enable it in
|
||||
config.yaml (``plugins.enabled: [pheby]``, ``platforms.pheby.enabled: true``),
|
||||
set ``PHEBY_SECRET`` in ``~/.hermes/.env``, and restart the gateway.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import os
|
||||
from typing import Any
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
__version__ = "1.0.0"
|
||||
|
||||
|
||||
def register(ctx: Any) -> None:
|
||||
"""Plugin entry point — called by the Hermes plugin system at startup."""
|
||||
from .adapter import PhebyAdapter
|
||||
from .config import (
|
||||
check_requirements,
|
||||
env_enablement,
|
||||
is_connected,
|
||||
validate_config,
|
||||
)
|
||||
|
||||
adapter_holder: dict = {"adapter": None}
|
||||
|
||||
def _factory(cfg: Any) -> PhebyAdapter:
|
||||
adapter = PhebyAdapter(cfg)
|
||||
adapter_holder["adapter"] = adapter
|
||||
return adapter
|
||||
|
||||
ctx.register_platform(
|
||||
name="pheby",
|
||||
label="Pheby",
|
||||
adapter_factory=_factory,
|
||||
check_fn=check_requirements,
|
||||
validate_config=validate_config,
|
||||
is_connected=is_connected,
|
||||
required_env=["PHEBY_SECRET"],
|
||||
install_hint="pip install aiohttp # already a Hermes dependency; "
|
||||
"set PHEBY_SECRET in ~/.hermes/.env",
|
||||
env_enablement_fn=env_enablement,
|
||||
# Home channel for cron / notification delivery when configured.
|
||||
cron_deliver_env_var="PHEBY_HOME_CHANNEL",
|
||||
allowed_users_env="PHEBY_ALLOWED_USERS",
|
||||
allow_all_env="PHEBY_ALLOW_ALL_USERS",
|
||||
emoji="🐱",
|
||||
pii_safe=True, # single-user private platform; no PII in routing IDs
|
||||
allow_update_command=True,
|
||||
platform_hint=(
|
||||
"You are communicating with the user via Pheby, a private "
|
||||
"native Android client over WebSocket. Respond in normal "
|
||||
"markdown; the client renders it natively. Attachments you "
|
||||
"produce via MEDIA: tags are delivered as downloadable files "
|
||||
"and inline image previews."
|
||||
),
|
||||
)
|
||||
|
||||
# post_tool_call observer → structured tool-result events. Registered
|
||||
# against the plugin context so it loads with the plugin, before any
|
||||
# adapter is constructed (the hook is a no-op until the adapter serves).
|
||||
def _post_tool_call(**kwargs: Any) -> None:
|
||||
adapter = adapter_holder.get("adapter")
|
||||
if adapter is not None:
|
||||
adapter.on_post_tool_call(**kwargs)
|
||||
|
||||
try:
|
||||
ctx.register_hook("post_tool_call", _post_tool_call)
|
||||
except Exception:
|
||||
logger.debug("[pheby] post_tool_call hook registration failed",
|
||||
exc_info=True)
|
||||
|
||||
logger.info("[pheby] plugin registered (platform 'pheby')")
|
||||
|
||||
|
||||
__all__ = ["register", "__version__"]
|
||||
Reference in New Issue
Block a user