feat(workspace): bake hermes-platform-molecule-a2a plugin into image - #32
Merged
Merged
Conversation
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>
This was referenced May 2, 2026
test(e2e): unified A2A round-trip parity harness across all 4 runtimes
EnterOS-AI/enter-os-core#2517
Merged
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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/completionsbridge.Changes
Dockerfile installs the patched hermes-agent fork + the
hermes-platform-molecule-a2aplugin into the venv created by the upstream installer. uv-created venvs don't seed pip, so weensurepipfirst thenpip installthegit+httpsrefs.start.sh seeds a
platforms.molecule-a2astanza into~/.hermes/config.yamlwhenMOLECULE_A2A_PLATFORM_ENABLED=true(default). Independent of the existing api-server platform on:8642— both run side-by-side.New env vars (defaults work out of the box):
MOLECULE_A2A_PLATFORM_ENABLED(defaulttrue)MOLECULE_A2A_PLATFORM_HOST(default127.0.0.1)MOLECULE_A2A_PLATFORM_PORT(default8645)MOLECULE_A2A_PLATFORM_CALLBACK_URL(defaulthttp://127.0.0.1:8000/a2a/reply)MOLECULE_A2A_PLATFORM_SHARED_SECRET(optional; empty = open localhost mode)Why pre-demo is install-only
adapter.pystill proxies A2A →/v1/chat/completionstoday. Switching it to POST/a2a/inboundagainst 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 perfeedback_runtime_publish_pipeline_gatesit's deferred until:Validation
docker build .on linux/amd64: pip install steps validated end-to-endpython3 -m ensurepip --upgrade✓--force-reinstall✓Successfully installed hermes-platform-molecule-a2a-0.1.0)bash -n start.shclean.Test plan
docker build .+docker runand confirm~/.hermes/config.yamlshows the molecule-a2a stanza.hermes gatewayboot log contains the linemolecule-a2a listening on http://127.0.0.1:8645/a2a/inbound.curl -fsS http://127.0.0.1:8645/a2a/healthreturns{"ok":true,"platform":"molecule-a2a"}from inside the container.curl -X POST http://127.0.0.1:8645/a2a/inboundwith a stub payload and confirm the platform-side callback receives the response.Once upstream merges
Drop
HERMES_FORK_REFand the--force-reinstallgit+https — plugin will load against the official wheel unchanged via thehermes_agent.pluginsentry point.🤖 Generated with Claude Code