Skip to content

feat(workspace): bake hermes-platform-molecule-a2a plugin into image - #32

Merged
HongmingWang-Rabbit merged 3 commits into
mainfrom
feat/molecule-a2a-platform-plugin
May 2, 2026
Merged

feat(workspace): bake hermes-platform-molecule-a2a plugin into image#32
HongmingWang-Rabbit merged 3 commits into
mainfrom
feat/molecule-a2a-platform-plugin

Conversation

@HongmingWang-Rabbit

Copy link
Copy Markdown
Contributor

Summary

Wires the upstream hermes-platform-plugin proposal end-to-end into the hermes workspace template. Pre-demo this lands the install + boot path only — the runtime adapter still uses the existing /v1/chat/completions bridge.

Changes

  1. Dockerfile installs the patched hermes-agent fork + the hermes-platform-molecule-a2a plugin into the venv created by the upstream installer. uv-created venvs don't seed pip, so we ensurepip first then pip install the git+https refs.

  2. start.sh seeds a platforms.molecule-a2a stanza into ~/.hermes/config.yaml when MOLECULE_A2A_PLATFORM_ENABLED=true (default). Independent of the existing api-server platform on :8642 — both run side-by-side.

  3. New env vars (defaults work out of the box):

    • MOLECULE_A2A_PLATFORM_ENABLED (default true)
    • MOLECULE_A2A_PLATFORM_HOST (default 127.0.0.1)
    • MOLECULE_A2A_PLATFORM_PORT (default 8645)
    • MOLECULE_A2A_PLATFORM_CALLBACK_URL (default http://127.0.0.1:8000/a2a/reply)
    • MOLECULE_A2A_PLATFORM_SHARED_SECRET (optional; empty = open localhost mode)

Why pre-demo is install-only

adapter.py still proxies A2A → /v1/chat/completions today. Switching it to POST /a2a/inbound against the plugin (the path that earns real session continuity instead of one stateless subprocess per message) is a separate change. That change touches a working live integration, so per feedback_runtime_publish_pipeline_gates it's deferred until:

  • Upstream PR #18775 merges
  • A hermes release containing the patch ships to PyPI
  • Runtime publish pipeline is green for adapter.py changes

Validation

  • docker build . on linux/amd64: pip install steps validated end-to-end
    • hermes installer ran (78s)
    • python3 -m ensurepip --upgrade
    • patched fork install via --force-reinstall
    • plugin install ✓ (Successfully installed hermes-platform-molecule-a2a-0.1.0)
    • Image extraction failed at the very last layer due to host disk at 100% — Dockerfile-side is sound.
  • Generated YAML parses under PyYAML.
  • bash -n start.sh clean.

Test plan

  • On a host with disk headroom: full docker build . + docker run and confirm ~/.hermes/config.yaml shows the molecule-a2a stanza.
  • Verify hermes gateway boot log contains the line molecule-a2a listening on http://127.0.0.1:8645/a2a/inbound.
  • curl -fsS http://127.0.0.1:8645/a2a/health returns {"ok":true,"platform":"molecule-a2a"} from inside the container.
  • Manual A2A round trip: curl -X POST http://127.0.0.1:8645/a2a/inbound with a stub payload and confirm the platform-side callback receives the response.

Once upstream merges

Drop HERMES_FORK_REF and the --force-reinstall git+https — plugin will load against the official wheel unchanged via the hermes_agent.plugins entry point.

🤖 Generated with Claude Code

Hongming Wang and others added 3 commits May 2, 2026 03:14
Wires the upstream hermes-platform-plugin proposal end-to-end into the
hermes workspace template. Two changes:

1. Dockerfile installs both the patched hermes-agent fork
   (NousResearch/hermes-agent#18775 — register_platform_adapter +
   GatewayConfig.plugin_platforms + PluginPlatformIdentifier +
   resolve_platform_id) and the hermes-platform-molecule-a2a plugin
   into the venv created by the upstream hermes installer. uv-created
   venvs don't seed pip, so we ensurepip first then pip install the
   git+https refs.

2. start.sh seeds a 'platforms.molecule-a2a' stanza into ~/.hermes/config.yaml
   when MOLECULE_A2A_PLATFORM_ENABLED=true (default). Independent of
   the existing api-server platform on :8642 — both run side-by-side.

Pre-demo deliberately does NOT change adapter.py: the runtime still
uses the api-server bridge today. Switching adapter.py to POST to the
plugin's /a2a/inbound endpoint (eliminating the cold /v1/chat/completions
subprocess-per-message path and earning real session continuity) is a
separate change post-demo, gated on the upstream PR merging and the
runtime publish pipeline being green for the change.

Validation: pip install steps verified end-to-end in 'docker build .'
on linux/amd64 (Successfully installed hermes-platform-molecule-a2a-0.1.0
+ patched hermes-agent fork). Image extraction failed at host-side
disk pressure (host at 100%), not a Dockerfile issue. Generated YAML
parses cleanly under PyYAML.

Once upstream PR #18775 merges + a release ships, the
'--force-reinstall' over the upstream wheel can be dropped and
HERMES_FORK_REF retired.
Replaces the synchronous /v1/chat/completions proxy with an async
plugin-path executor that earns single-session continuity for peer
agents.

Behavior:
  - Default: POST each A2A turn to the in-container hermes plugin's
    /a2a/inbound; await the agent reply via an aiohttp callback server
    inside the executor. The plugin POSTs hermes's reply back to the
    callback server, correlated by message_id, which resolves the
    awaiting Future and emits on the A2A queue.
  - Fallback: MOLECULE_A2A_PLATFORM_ENABLED=false reverts to the
    legacy /v1/chat/completions transport — same behavior as before
    this commit. Lets operators flip the path off if the plugin path
    misbehaves in production.

Wire shape:
  - Plugin's adapter.send(chat_id, content, reply_to, metadata)
    becomes POST <callback_url> with the same fields.
  - Correlation is by reply_to (= the inbound message_id), not by
    chat_id — two in-flight messages on the same chat would race on
    the latter.
  - Optional MOLECULE_A2A_PLATFORM_SHARED_SECRET is sent on outbound
    POSTs and required on inbound replies.

Tests: 36 unit tests, 98% combined coverage on adapter.py + executor.py.
Covers lifecycle (start/stop/idempotent), happy path (round-trip
through stub plugin), error paths (POST failure, reply timeout, late
delivery for unknown message_id, malformed JSON, missing fields),
auth (shared_secret enforcement both directions), fallback (chat
completions HTTP error, unreachable port, junk response shape), and
chat_id derivation precedence.

Real-LLM E2E remains gated on docker image republish + workspace
provisioning + LLM key — the unit tests bound the wire-shape risk
and the existing scripts/e2e_real_hermes_subprocess.py in
hermes-platform-molecule-a2a covers the plugin side end-to-end against
a real `hermes gateway run` subprocess.
…bprocess

Spawns a real hermes gateway run + a stub OpenAI-compat LLM server +
the real executor's reply server, and routes a message through every
hop of the production chain except platform-side peer-message routing:

  HermesAgentProxyExecutor.execute()
    → POST /a2a/inbound (hermes plugin)
      → MessageEvent dispatch through hermes pipeline
        → stub LLM /v1/chat/completions
      → plugin send() POSTs reply to executor /a2a/reply
    → execute() Future resolves → emits on event_queue

This is the highest-fidelity local approximation of staging E2E.
Caught a real KeyError in upstream hermes hermes_cli/tools_config.py
that no in-process test surfaced. Asserts the wire shape works end to
end + guards against the KeyError regression. The reply CONTENT
depends on whether the stub speaks hermes' multi-turn tool loop, so
we don't assert on it — what matters is the full pipeline routes
through the plugin and back.

Run:
  /Users/hongming/.hermes/hermes-agent/venv/bin/python3 \\
      scripts/e2e_full_chain.py

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant