Go to file
Hongming Wang b6d67520cf feat: dual-mode for upstream register_platform (post-#17751) + legacy register_platform_adapter
Same migration shape as Molecule-AI/hermes-channel-molecule@754d162.

NousResearch/hermes-agent#17751 (merged 2026-04-30) shipped a
comprehensive pluggable-platform system with:
- ctx.register_platform(name, label, adapter_factory, check_fn, ...)
- Open Platform enum (Platform('molecule-a2a') via _missing_())

Pre-#17751 forks (incl. the patched hermes-agent baked into
template-hermes' Dockerfile today) expose ctx.register_platform_adapter
+ closed Platform + PluginPlatformIdentifier instead. Detect at runtime
in both register() and the adapter's super().__init__ identity arg so
the same wheel installs cleanly on both shapes.

template-hermes can now drop its hermes-agent fork install and use
upstream main once the in-container tests pass on stock hermes-agent.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 07:50:18 -07:00
hermes_platform_molecule_a2a feat: dual-mode for upstream register_platform (post-#17751) + legacy register_platform_adapter 2026-05-03 07:50:18 -07:00
scripts test: extend E2E + add real-subprocess validation 2026-05-02 03:17:57 -07:00
tests Initial: hermes-platform-molecule-a2a v0.1.0 2026-05-02 03:06:56 -07:00
.gitignore Initial: hermes-platform-molecule-a2a v0.1.0 2026-05-02 03:06:56 -07:00
pyproject.toml Initial: hermes-platform-molecule-a2a v0.1.0 2026-05-02 03:06:56 -07:00
README.md Initial: hermes-platform-molecule-a2a v0.1.0 2026-05-02 03:06:56 -07:00

hermes-platform-molecule-a2a

A hermes-agent platform adapter that delivers Molecule A2A peer-agent messages to a running hermes daemon over HTTP, and POSTs agent replies back to the molecule-runtime callback URL.

Why this exists

Every Molecule workspace runtime needs MCP-style "push" parity: a single long-lived agent session that receives new peer messages mid-thread instead of cold-starting one process per message.

  • claude-code: native MCP notifications/claude/channel
  • codex: persistent codex app-server JSON-RPC stdio
  • hermes: this plugin (HTTP listener inside the hermes gateway)

Without this, hermes workspaces lose conversation continuity (every A2A message hits stateless /v1/chat/completions) and pay subprocess startup cost on every peer message.

Requires

This plugin depends on three small additions to hermes-agent that are not yet upstream:

  1. PluginContext.register_platform_adapter(name, adapter_class, requirements_check=None)
  2. GatewayConfig.plugin_platforms: Dict[str, PlatformConfig], populated by from_dict for any platform name claimed by a discovered plugin.
  3. GatewayRunner._create_plugin_adapter(name, config) boot path.

Until they land, install the plugin against a hermes fork carrying feat/platform-adapter-plugins. See docs/integrations/hermes-platform-plugins-upstream-pr.md in molecule-core for the upstream proposal.

Install

pip install hermes-platform-molecule-a2a

The plugin auto-registers via the hermes_agent.plugins entry point. No imports or wiring on the hermes side are needed beyond enabling the platform in config.

Configure

In your hermes config.yaml:

platforms:
  molecule-a2a:
    enabled: true
    extra:
      host: "127.0.0.1"             # default
      port: 8645                     # default
      callback_url: "http://runtime:9999/a2a/reply"
      shared_secret: "..."           # required in production; empty
                                     # = open mode for localhost only

When the molecule-runtime POSTs an inbound A2A message and includes its own callback_url in the payload, that overrides the default for that chat — letting the runtime route different peer threads to different callback endpoints.

Wire shape

Inbound (runtime → hermes)

POST /a2a/inbound
X-Molecule-A2A-Secret: <shared_secret>
Content-Type: application/json

{
  "chat_id": "peer-uuid",
  "peer_id": "peer-uuid",
  "peer_name": "ops-agent",
  "peer_role": "sre",
  "content": "the message text",
  "message_id": "uuid-or-monotonic",
  "callback_url": "http://runtime:9999/a2a/reply",
  "thread_id": "optional"
}

→ 200 {"ok": true, "queued": true}

The handler returns immediately; hermes processes the message in a background task and delivers the reply through send().

Outbound (hermes → runtime)

POST <callback_url>
X-Molecule-A2A-Secret: <shared_secret>
Content-Type: application/json

{
  "chat_id": "peer-uuid",
  "content": "agent reply text",
  "reply_to": "msg-id-of-inbound",
  "metadata": {...}
}

Validate

Unit tests (no hermes runtime required):

pip install -e '.[test]'
pytest tests/ -q

End-to-end against a hermes-agent install with the patch:

HERMES_REPO=/path/to/hermes-agent python scripts/e2e_validate.py

The E2E script confirms the entry-points discovery → platform registry → GatewayConfig routing → adapter boot → HTTP roundtrip pipeline.

License

MIT