diff --git a/gateway/config.py b/gateway/config.py index 2bf2bc67..0ac10e0e 100644 --- a/gateway/config.py +++ b/gateway/config.py @@ -90,8 +90,9 @@ class HomeChannel: @classmethod def from_dict(cls, data: Dict[str, Any]) -> "HomeChannel": + from hermes_cli.plugins import resolve_platform_id return cls( - platform=Platform(data["platform"]), + platform=resolve_platform_id(data["platform"]), chat_id=str(data["chat_id"]), name=data.get("name", "Home"), ) diff --git a/gateway/session.py b/gateway/session.py index c14e9bd0..af00170f 100644 --- a/gateway/session.py +++ b/gateway/session.py @@ -123,8 +123,9 @@ class SessionSource: @classmethod def from_dict(cls, data: Dict[str, Any]) -> "SessionSource": + from hermes_cli.plugins import resolve_platform_id return cls( - platform=Platform(data["platform"]), + platform=resolve_platform_id(data["platform"]), chat_id=str(data["chat_id"]), chat_name=data.get("chat_name"), chat_type=data.get("chat_type", "dm"), @@ -409,10 +410,8 @@ class SessionEntry: platform = None if data.get("platform"): - try: - platform = Platform(data["platform"]) - except ValueError as e: - logger.debug("Unknown platform value %r: %s", data["platform"], e) + from hermes_cli.plugins import resolve_platform_id + platform = resolve_platform_id(data["platform"]) return cls( session_key=data["session_key"], diff --git a/hermes_cli/plugins.py b/hermes_cli/plugins.py index 99b708f9..4aa41b55 100644 --- a/hermes_cli/plugins.py +++ b/hermes_cli/plugins.py @@ -883,6 +883,32 @@ class PluginPlatformIdentifier: return f"PluginPlatformIdentifier({self.value!r})" +def resolve_platform_id(value: str): + """Resolve a platform-name string to a ``Platform`` enum member or + a ``PluginPlatformIdentifier``. + + Used by ``from_dict`` deserializers for ``SessionSource``, + ``SessionEntry``, ``HomeChannel`` etc. Without this fallback, + daemon restart would lose every session whose platform was + contributed by a plugin (e.g., ``molecule-a2a``) because the + bare ``Platform(value)`` call raises ``ValueError`` for unknown + enum members. + + The fallback is intentionally narrow: an unknown name is only + accepted as a plugin identifier if a currently-loaded plugin has + actually claimed that name via ``register_platform_adapter``. Bare + typos and corrupted state still raise ``ValueError`` as before, so + silent state-corruption doesn't slip past restore. + """ + from gateway.config import Platform # late import to avoid cycle + try: + return Platform(value) + except ValueError: + if get_plugin_platform_adapter(value) is not None: + return PluginPlatformIdentifier(value) + raise + + def get_plugin_commands() -> Dict[str, dict]: """Return the full plugin commands dict (name → {handler, description, plugin}).