Skip to main content

wishee-hermes-connector

Connect a Hermes agent to the Wishee bridge (openclaw-bridge) over a reverse WebSocket — the Hermes counterpart of @wishee-ai/openclaw-connector. The bridge drives the agent (chat.send / chat.history / sessions.reset) and receives its replies (task-result).

Requires no changes to the bridge or to hermes-agent core.

Requires Hermes >= 0.14.0. The connector checks this in register() and refuses to load below it — Hermes surfaces the error in the gateway log and in hermes plugins list. See version_gate.py for how that floor was derived (the binding constraint is PlatformEntry.is_connected, 2026-04-20).

How it works

The connector is a Hermes platform adapter (runs inside the hermes gateway process). It dials the bridge, does the hello/welcome handshake, then:

  • chat.send → mints a runId, answers immediately, runs the agent turn, and emits a task-result event when the turn finishes (the bridge's device-push path).
  • chat.history / sessions.reset → reads / clears the Hermes session store.

From the bridge's perspective a Hermes agent is indistinguishable from an OpenClaw instance: same /api/bind, same hello, same RPCs, same event frames.

Activity (usage duration)

The connector installs a Hermes hook at ~/.hermes/hooks/wishee-activity/ that listens for agent:start / agent:end — the two events that bracket one _run_agent call, i.e. the real boundary of a turn, for every platform. So turns the user starts locally count too, not just the ones the bridge dispatches.

The hook handler only appends one self-contained interval per finished run to a local journal (~/.hermes/wishee-connector/wishee-activity-YYYYMMDD.jsonl); it never imports the connector, touches the network, or starts a timer. The connector reads that journal by byte watermark and ships batches as agent-activity frames, advancing the watermark only after a successful send — the journal is what makes this survive a disconnect or a restart (unlike an in-memory queue).

On first run it also seeds the journal once from state.db, deriving past turns from message timestamps, so history predating the hook isn't lost.

The gateway only scans hooks/ at startup, and the connector writes the hook when it connects — later than that scan. Up to 0.13.0 a fresh hook therefore took a second restart to start recording. Since 0.14.0 the connector also registers it into the running gateway's hook registry when it connects (both at startup and after a hot attach, see below), skipping it when the gateway already loaded it by name. A hook whose handler changed in an upgrade still switches over on the next restart.

阻塞式提示(询问选项 / 危险命令审批)

Hermes 的两类会卡住 agent 线程等用户的提示——clarify(提问 + 选项)与 危险命令审批——都经 bridge 现成的输入类审批通道推到 设备:approval-request{kind:"user_input", inputSpec} → 设备渲染 → approval.respond{approvalId, decision, input} → approval-resolved。不新开 帧,所以 bridge 和 profile-service 无需改动。

提问卡的 inputSpec 与 codex 连接器的 requestUserInput 同构(questions[], 契约 v2),这样主机端只做一种渲染——见 docs/superpowers/specs/2026-09-11-user-input-card-contract.md。危险命令审批那半 (kind:"command")不变,见 docs/superpowers/specs/2026-09-10-approval-roundtrip-contract.md。

不实现这两个方法的话都会落到 Hermes 的文本兜底:问题被拼成编号文本走 send(),而 send() 只往 _pending_reply 里塞,要等整轮跑完才发——可这一轮 正卡在等回答上(默认 3600 秒)。主机端因此整整一小时收不到任何一帧,日志里 还什么都不报,因为兜底照样回 success=True。

一轮结束时还挂着的问题会回一条 approval-resolved{outcome:"canceled"},设备 据此撤掉卡片——超时之后 agent 早就自己往下跑了,那张卡再答也没有去处。0.10.0 起 不必等到一轮结束:连接器每 3 秒看一眼 Hermes 还等不等设备上挂着的提示(审批超时、 clarify 超时、在同一会话里打 /approve /deny 答掉),不等了就立刻撤卡。

危险命令审批卡的按钮由 bridge 按 rawParams.availableDecisions 出(codex 的词汇), 连接器据 Hermes 的 allow_session 带上 acceptForSession,设备上就有「允许本次会话」; 「始终允许」故意不给(设备那页永远不发 approve_always)。Hermes 0.21 起每条待批有 request_id,连接器按它批,同一会话里同时挂着两条时不会批错(老版本只能 FIFO)。 Hermes 0.21.5 起网关只给声明了 supports_exec_approval_buttons() 的平台发审批卡, 连接器声明了。

0.11.0 起:

  • Hermes 0.21.x 收掉一个没答的问题时会调 retire_clarify_card,设备上的卡当场撤掉 (老版本仍靠上面那个 3 秒巡检);提问卡发失败后网关补发的纯文本版如实报失败, agent 不会按"已送达"干等一小时。
  • 推出去还没收尾的卡记在 <HERMES_HOME>/wishee-connector/pending-cards.json:Hermes 重启(升级、崩溃、OOM)后那几轮已经没了,或者收尾那一刻正好和 bridge 断着,下次连上 bridge 时补发 canceled 把作废的卡撤掉。

执行过程(0.10.0)

与 codex 连接器同一套契约,设备上「正在执行任务…」那一行、锁屏卡、过程播报都靠它:

  • agent-progress{sessionKey, itemId, ts} —— agent 说完一段话、后面还要调工具时发, 只带信号不带正文。来源是 post_api_request 插件钩子(0.14.0 起就有):带工具调用 的那次模型返回一定不是结论,所以一到就报,不受 display.* 配置影响;正文按 Hermes 自己的 strip_think_blocks 洗掉推理块。不用 send() 当来源:流式 / previewed 时 结论也走它,0.14 上心跳和中间消息又分不出来。只报人话:去掉代码之后一个字都不剩 的不算过程(口径同 codex 连接器)。Hermes 0.21.x + Codex 类 provider(openai-codex) 的 content 是空的,那句话在 codex_message_items 的 commentary 里,按 Hermes 自己界面 那套(visible_commentary:去推理块 + 脱敏)取。
  • chat.turns{sessionKey, limit} —— 这条会话最近若干轮(一条用户消息 + 它之后的 agent 回复),每条消息带 id、每轮带 status(inProgress / completed / failed)。 跑完的轮次读 state.db;在跑的那一轮只认内存——0.14–0.16 整轮跑完才落盘、0.18 起 边跑边写,盘上那份要么没有要么只有半截。开跑时记下全库 messages.id 的水位,比它 大的行就是这一轮的;跑完后按正文把过程的 itemId 对回去,推送晚到也找得到。 chat.history 原样保留给老设备。

The transport auto-reconnects: an unexpected socket close (bridge restart, network drop) re-dials with capped exponential backoff and re-runs the hello/welcome handshake. A deliberate shutdown never reconnects.

装完不用重启(0.14.0)

Hermes 只在网关启动时给平台建适配器,所以以前装完必须请用户发 /restart(agent 自己 重启不了网关,Hermes 硬拦)。Hermes 0.21.5 起,hermes plugins enable/install 会经 控制 socket 让正在跑的网关重新扫描插件,register() 因此在网关进程里被执行——但网关 只给已有适配器重新接线,不建新平台。连接器在这时把 wishee 塞进网关自己的平台重连队列 (live_gateway.py),由重连巡检按启动时同一套流程建适配器、接 handler、连 bridge,一两 秒内就连上,待认领的绑定码也在这时认领掉。

  • 只在 config.yaml 里有 platforms.wishee.enabled: true 时才挂——和重启后的行为一致, 免得现在能用、下次重启悄悄不见。
  • 网关启动阶段加载插件时什么都不做(_running 要等所有平台连完才置上),交给启动流程。
  • 已经在线或已在队列里就不动:装别的插件也会触发强制重新扫描,不能挂出第二个适配器。
  • 碰的是 Hermes 的私有属性(_gateway_runner_ref、_failed_platforms、 _reconnect_watcher_task),每样先探测,缺了就打一行日志、退回"请重启",不让插件加载失败。
  • register() 是在插件发现锁里被调的,不能用 load_gateway_config() 读配置——它会 discover_plugins(),等的就是这把锁;0.21.5 真网关上实测 register() 卡满 10 秒被判加载 失败。所以直接读 config.yaml,用网关同一个 PlatformConfig.from_dict 解析。

做不到的:Hermes 0.21.4 及以下没有运行中重新扫描,照旧要重启;升级(pip install -U) 也照旧要重启——pip 装的插件重新扫描时不会重新 import,跑的还是旧代码。

Install

pip install wishee-hermes-connector   # into your Hermes environment

Enable

  1. Set platforms.wishee.enabled: true in your Hermes config.yaml (platform adapters are off by default), and write the bind code if there is one (item 5). Do both before item 2: on Hermes ≥ 0.21.5 item 2 starts the connector at once.
  2. Run hermes plugins enable wishee (entry-point plugins are opt-in). It adds wishee to plugins.enabled; on Hermes ≥ 0.21.5 with a running gateway it also loads the plugin into that gateway and the connector attaches itself — no restart (see 装完不用重启 above). Hermes 0.14's CLI does not know entry-point plugins ("is not installed or bundled"): add wishee to plugins.enabled by hand.
  3. Optionally set WISHEE_BRIDGE_URL to override the default (wss://ai-bridge.wishee.com.cn/ws).
  4. If the connector did not start in item 2 (older Hermes, or no gateway was running), restart / run hermes gateway. On first run it creates this instance's identity; while unbound it logs a hint telling the user to scan the QR on the WISHEE device (「My Agent」→ Hermes) and send the resulting message to the agent. Up to 0.12.0 it also printed a QR for the bridge's scan-to-bind web page; 0.13.0 drops that — binding goes only through the bind code below. Identity is stored at <HERMES_HOME>/wishee-connector/binding.json, i.e. ~/.hermes/... unless HERMES_HOME says otherwise — the same directory the activity journal uses. Up to 0.8.0 this one path instead read HOME and fell back to /tmp, which on Windows (where a gateway cold start usually has no HOME) put the identity in C:\tmp and made the agent look unbound after an upgrade. 0.9.0 adopts an identity left at that old location on first run, copying it rather than moving it so a rollback still finds one.
  5. Bind code (0.12.0+). When the user has an 8-letter WISHEE bind code (sent by the WeChat account after scanning the QR on the WISHEE device), write it to <HERMES_HOME>/wishee-connector/pending-bind-code before the connector starts. When it starts (hot attach or restart) it claims the code itself via the bridge's POST /api/bind/claim (same request the onboarding doc's curl sends: body = binding.json), so on Hermes ≥ 0.21.5 nothing else is needed and on older ones the user only has to send /restart — an agent cannot restart its own gateway (Hermes blocks it). The outcome is written to claim-result.json in the same directory (no secret in it). Definitive failures (code used / expired / unknown / identity conflict) delete the code file; transient ones (bridge unavailable, rate limited, network) keep it and retry while waiting to be bound. Local split-port dev setups can point the claim at the HTTP port with WISHEE_CLAIM_URL.

Scope

Emits task-result on each completed turn (the bridge's device-push path), and since 0.10.0 agent-progress signals + chat.turns for the in-turn progress (see above). Token-level streaming is still out of scope: the device only needs whole lines.

Development

Tested against Hermes 0.14.0 (tag v2026.5.16, what the oldest supported installs run), 0.21.0 (tag v2026.8.31) and 0.21.5 (tag v2026.9.24, the device end-to-end run of 0.11.0). A few tests skip themselves on 0.14 where the feature does not exist there (multi-select clarify, approval request_id).

The hot attach (0.14.0) was also run end to end against real gateways with a fake bridge: on 0.21.5, installing + hermes plugins enable wishee into a running gateway connected and claimed the bind code with the gateway PID unchanged, and a second forced plugin rescan left the one adapter alone; on 0.14.0, enable refuses the entry-point plugin, nothing attaches, and after a restart it connects and claims as before.

uv venv --python 3.13 .venv
uv pip install --python .venv/bin/python -e /path/to/hermes-agent
uv pip install --python .venv/bin/python -e ".[dev]"
.venv/bin/python -m pytest -q

Metadata

Release files for wishee-hermes-connector 0.14.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for wishee-hermes-connector 0.14.0
File Size Uploaded
wishee_hermes_connector-0.14.0.tar.gz 120.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for wishee-hermes-connector 0.14.0
File Interpreter ABI Platform
wishee_hermes_connector-0.14.0-py3-none-any.whl Python 3 none any Details

Total release size: 202.8 kB

Release files / wishee_hermes_connector-0.14.0.tar.gz

Download URL wishee_hermes_connector-0.14.0.tar.gz
Size 120.9 kB
Tags Source
SHA-256 checksum
How to use checksums
9c5137ea5cee69517c09ce50ac121e10e094558246fdf58931e0e8be631b8e29
BLAKE2b-256 checksum
How to use checksums
91da4ebf1bd138b1ef855ed637de8d2619b7c5a987c067bb5ac95f5a7477a116
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.8.22

Release files / wishee_hermes_connector-0.14.0-py3-none-any.whl

Download URL wishee_hermes_connector-0.14.0-py3-none-any.whl
Size 81.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
8ed75d79612970d53a94a9dc654896e6b37c66ff37f39c0ad0f2f1f0477a4021
BLAKE2b-256 checksum
How to use checksums
84b35ff2d542b65cb9e9f5b3e304ff74f5212be969c90eb9c2020c0f63fdfb37
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.8.22

Release history Release notifications | RSS feed

0.15.0

2 release files

This release

0.14.0 This release

2 release files

0.9.0

2 release files

0.8.0

2 release files

0.7.0

1 release file

0.6.0

1 release file

0.5.1

2 release files

0.4.0

2 release files

0.2.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page