Files
pheby-hermes-platform-adapter/plugin/pheby/__init__.py
T
2026-09-02 13:59:00 -07:00

85 lines
2.9 KiB
Python

"""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
from typing import Any
logger = logging.getLogger(__name__)
__version__ = "1.0.1"
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",
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."
),
)
# Tool observers provide authoritative session_id + tool_call_id values.
# The adapter's presentation-only format_tool_event hook deliberately eats
# Hermes's generic tool chrome to avoid duplicate/ambiguous events.
def _pre_tool_call(**kwargs: Any) -> None:
adapter = adapter_holder.get("adapter")
if adapter is not None:
adapter.on_pre_tool_call(**kwargs)
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("pre_tool_call", _pre_tool_call)
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__"]