feat(hermes-bridge): bridge W2A sensors to Hermes via webhook subscriptions#5
Draft
feat(hermes-bridge): bridge W2A sensors to Hermes via webhook subscriptions#5
Conversation
…ptions Adds @world2agent/hermes-sensor-bridge: an independent local supervisor daemon that hosts W2A sensor subprocesses and ships their signals into Hermes Agent via the gateway's native webhook subscriptions. End-to-end exercised against a real `hermes gateway run` with @world2agent/sensor- hackernews — real HN signals delivered, HMAC validated, X-Request-ID dedup verified, and Hermes ran AIAgent.run_conversation() per signal with the generated handler skill auto-loaded. Components: - runner: thin Node subprocess running a sensor with SDK stdoutTransport. Channel-agnostic — ships every signal as a JSON line on stdout, all diagnostics on stderr. No Hermes knowledge. - supervisor daemon: spawns/monitors runners, parses stdout signals, POSTs each with X-Webhook-Signature (raw hex HMAC-SHA256) + X-Request-ID (= signal.signal_id). Exposes 127.0.0.1 control HTTP for reload/list/health. HMAC and webhook URLs stay in supervisor only, never leak into child env. - CLI `world2agent-hermes`: start/stop/status/list/add/remove/logs + hermes-init (auto-bootstraps Hermes webhook platform — writes a managed `platforms.webhook` block to ~/.hermes/config.yaml and WEBHOOK_* to ~/.hermes/.env, marker-fenced and idempotent). - world2agent-manage skill: agent-facing wrapper around the CLI. Depends on the public @world2agent/sdk@0.1.0-alpha.1 — uses only existing exports (startSensor, FileSensorStore, stdoutTransport, packageToSkillId). No SDK changes required for this PR. Status: WIP. Smoke tests for the bootstrap helper (4 states) and the delivery worker (HMAC, X-Request-ID, body shape, 4xx/5xx behaviour) pass via `node e2e/test-*.mjs` after `pnpm run build`. Known follow-ups: - Codex review feedback partially addressed: parseSubscribeOutput no longer synthesizes a default name; manifest preserves user-provided skill_id; supervisor's NO_RESTART exit-code set is honest; helper heals partial-state .env when config.yaml says enabled. - Manifest write conflict if another in-process consumer also writes to ~/.world2agent/sensors.json with a different schema (encountered during E2E with a parallel openclaw-plugin spike). Not addressed. - Interactive setup not implemented; --config-file required. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…tion The plugin marketplace loads packages from this repo, not from npm, so shipping dist/ with the source is the established convention. Drops `dist` from .gitignore and adds the built artifacts. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…l convention" This reverts commit 6867699.
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
New package
@world2agent/hermes-sensor-bridge— an independent local supervisor daemon that hosts W2A sensor subprocesses and ships their signals into Hermes Agent via the gateway's native webhook subscriptions. Sibling ofclaude-code-channel, but Hermes-shaped (out-of-process bridge, fresh AIAgent run per signal, no MCP).End-to-end exercised against a real
hermes gateway runwith@world2agent/sensor-hackernews: real HN signals through sensor → supervisor → Hermes → AIAgent — HMAC verified, X-Request-ID dedup verified, real LLM responses generated.Architecture (high level)
Sensor child has zero Hermes knowledge — it's a stock
startSensor+ SDKstdoutTransport. All Hermes-specific work (HMAC, X-Request-ID, prompt body shape, HTTP retries, route subscription, platform bootstrap) lives in the supervisor. Same runner can be reused by future channels.What this PR adds
src/runner/: bin that runs one sensor and writes signals to stdout, logs to stderr.src/supervisor/: daemon. Spawns/monitors runners, parses stdout signals, POSTs to Hermes with HMAC + X-Request-ID, retries on 5xx/network. Exposes 127.0.0.1 control HTTP for reload/list/health.src/cli/:world2agent-hermesuser CLI —start/stop/status/list/add/remove/logsplushermes-init.skills/world2agent-manage/: agent-facing skill wrapping the CLI for natural-language sensor management.hermes-init(auto-called byadd): writes a marker-fenced managedplatforms.webhookblock to~/.hermes/config.yamlandWEBHOOK_*to~/.hermes/.env. Idempotent. Refuses to merge into a hand-managed top-levelplatforms:block.e2e/: smoke tests for the bootstrap helper (4 states) and delivery worker (HMAC raw hex, X-Request-ID, body shape, 4xx fail-fast, 5xx retry).Dependencies
@world2agent/sdk@0.1.0-alpha.1(already published). No SDK changes required for this PR — bridge uses existingstartSensor,FileSensorStore,stdoutTransport,packageToSkillId.hermesCLI on PATH foradd/remove(which shell out tohermes webhook subscribe/unsubscribe).Status: WIP
Codex review fed back several issues; most are addressed in this branch:
parseSubscribeOutputno longer synthesizes a default subscription name; caller falls back to the name it requested.skill_id(was previously overridden bypackageToSkillId(pkg)in normalize/parse).NO_RESTART_EXIT_CODESno longer lists exit 13 (the runner has no startup self-test that produces it; was dead-code).ensureHermesWebhookEnabledheals.enveven when config.yaml already says enabled (partial-state recovery).addnow passes the bridge HMAC secret through tohermes-initso Hermes's globalWEBHOOK_SECRETmatches~/.world2agent/.hmac_secreton first install.Open items for follow-up:
~/.world2agent/sensors.jsonwith a different shape (nowebhook_url). The bridge currently rejects on parse — needs a per-channel namespace or owner key.add(Q&A driven by the sensor'sSETUP.md) is not implemented;--config-file <path>is required for now. Bridge writes a generic handler skill in this case rather than the sensor-specific one.node e2e/*.mjssmoke tests).Test plan
pnpm run build(TypeScript strict, ESM)node e2e/test-ensure-hermes-webhook.mjs(23/23 PASS — fresh / idempotent / pre-enabled / unmanaged-refuses / partial-heal / nested-agent.platforms-ignored / skill_id round-trip)node e2e/test-delivery.mjs(16/16 PASS — happy path, 4xx fail-fast, 5xx retry exhaust, 5xx-then-200)hermes gateway runwith@world2agent/sensor-hackernews: 9 real signals delivered with HTTP 202, X-Request-ID = signal.signal_id, distinct deliveries triggered AIAgent runs producing real LLM responses (kimi-k2.6 + gemini-3-flash-preview)🤖 Generated with Claude Code