Reverts the temporary file-based [phone-diag] scaffolding (connect/loop/register
tracing) added to localize the two-way-reply failure. The genuine fixes stay:
PhoneAdapter.connect(is_reconnect=...) (3018a18), and the registration `except`
upgraded from silent debug to a visible warning.
- DEVLOG: record the device round-trip + both faults — the connect() signature
TypeError, and a stale duplicate plugin copy in the user-plugins dir winning the
loader's name-dedup so the gateway loaded old code and ignored every deploy.
- TODO: mark Phase 2c device-verified; add the installer-purge-stale-backups fix
and the usability roadmap (outbound buffering first; threaded agent surface per
the "separate lanes, unified surface" decision; per-thread chat_id; status; title).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The gateway journal suppresses plugin logs at every level, so the WARNING
trace was invisible. Switch the diagnostics to append to ~/.hermes/phone-diag.log
so we can see exactly which hop fires: registration, connect() entry/abort,
reply-loop start. TEMPORARY — reverted once the connect gap is fixed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
TEMPORARY diagnostics (to be reverted) to localize why the gateway's phone
adapter never polls /phone/replies despite registration+env_enablement working
in isolation. Plugin INFO is filtered from the gateway journal, so these trace
at WARNING: registration outcome (was silently swallowed), connect() entry, and
reply-loop start. Also upgrades the previously-silent registration except from
DEBUG to WARNING — a real improvement worth keeping.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a push-only BasePlatformAdapter (PhoneAdapter) registered via the
upstream platform-plugin API so the agent can proactively message the
paired device: send_message target=phone and cron deliver=phone. send()
POSTs loopback to the relay (/phone/message); the relay forwards over the
existing phone WSS. Off by default behind PHONE_ENABLED; no fork.
Modeled on the bundled ntfy adapter, including a standalone_sender_fn so
out-of-process cron delivery works. Plugin stays kind=standalone;
registration wired into register() and guarded for older hosts.
22 unit tests cover gating, relay-URL precedence, payload build, and the
standalone sender over a fake httpx client.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adopt four upstream plugin surfaces for easier setup/use:
- requires_env rich form: declare the optional voice-provider keys (XAI/OpenAI/
ElevenLabs) so `hermes plugins install` prompts for them with a "get yours"
link instead of hand-editing ~/.hermes/.env. Standard path needs none.
- Native install: document/support `hermes plugins install
Codename-11/hermes-relay/plugin` for tools-only setups (additive; the full
relay still uses the curl install.sh).
- /relay slash commands (status/devices/pair) usable mid-chat from any platform,
reusing existing relay logic; every path guarded.
- A minimal on_session_start hook: one 0.5s-timeout guarded /health ping,
returns None, can't slow or crash the gateway.
Verified against upstream/main plugin contract (register_command, register_hook
on_session_start, requires_env shape, plugins install subdir).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Closes the drift between hermes-host's running hermes-relay and what
main had committed. Three sources converge here:
(1) feature/desktop-tui-mvp (3 commits never merged to main):
- plugin/relay/channels/tui.py (508 LOC) — THE tui channel
handler that spawns tui_gateway. Main has been running on the
server but absent from git for a week.
- docs/relay-protocol.md (450 LOC) — formal WSS envelope spec.
- plugin/tests/test_tui_channel.py (520 LOC).
- scripts/tui-smoke{,-teardown}.sh.
- plugin/relay/auth.py +4 lines.
- 4-line addition to plugin/relay/server.py for tui dispatch.
(2) alpha.1 hot-fix drift (live on the server, untracked in git):
- plugin/relay/channels/desktop.py (424 LOC) — Phase B tool
command channel: desktop.command/response/status, UUID-future
correlation, single-client MVP. MERGED with the alpha.6
DesktopChannel (161 LOC, workspace-awareness) into one
DesktopHandler class. Backwards-compat alias
`DesktopChannel = DesktopHandler` preserves alpha.6 import
sites in server.py.
- plugin/tools/desktop_tool.py (349 LOC) — registers 5 desktop_*
tools (read_file/write_file/terminal/search_files/patch) via
tools.registry. Adopted verbatim from server's working tree.
- plugin/__init__.py — extended to register desktop tools via the
plugin context API + matching plugins.enabled documentation.
Adopted verbatim from server.
(3) Conflict resolution in server.py:
- bridge.close() → desktop.close() → tui.close() lifecycle
(both alpha.1's desktop hook and feature branch's tui hook
called during shutdown).
- _on_disconnect: server.desktop.detach_ws(ws) +
server.tui.detach_ws(ws, reason=...) both run on disconnect.
- alpha.9 /clipboard/inbox endpoint preserved in route table.
After this lands on main, hermes-host can `git checkout -- .`
(its working tree drift now matches main verbatim) and `git pull
origin main --ff-only` cleanly.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three parallel workstreams landed together.
## 1. Skill authoring — new /hermes-relay-pair slash command
skills/hermes-relay-pair/SKILL.md (98 lines, agentskills.io-compatible):
- Proper YAML frontmatter (name, description, version, author, license,
platforms: [linux, macos], metadata.hermes tags/category/homepage)
- Body sections: When to Use / Prerequisites / Procedure / Pitfalls /
Verification — terse imperatives suitable for the agent's context window
- Tells the agent to run `python -m plugin.pair`, explains the venv path
trap, walks through host-side verification (curl /health, clients
count), warns about code expiration + QR terminal rendering gotchas
- Once installed to ~/.hermes/skills/, auto-registers as the
/hermes-relay-pair slash command in every chat session + messaging
platform.
## 2. Rename hermes-android → hermes-relay (user-facing, not Python)
The repo was rebranded on 2026-04-08 but the Python *package* / plugin
distribution name still said `hermes-android`. It's visible in
`hermes plugins list`, pypi-style imports, and a dozen user-docs /
README / install-snippet references. Renamed everywhere it's a package
name or user-visible string; Python module path `plugin` stays exactly
as-is (imports unchanged).
- pyproject.toml: name "hermes-android" → "hermes-relay",
version "0.4.0" → "0.5.0", description rewritten
- plugin/plugin.yaml: name + version aligned with pyproject
- plugin/__init__.py: docstring updated
- install.sh: PLUGIN_NAME target dir is now ~/.hermes/plugins/hermes-relay
- README.md, AGENTS.md, docs/{security,relay-server,upstream-contributions}.md,
user-docs/guide/getting-started.md, user-docs/reference/{relay-server,configuration}.md:
install snippets + package references swept
- plugin/{android_tool,tools/android_tool}.py: package name strings updated
- relay_server/SKILL.md, skills/hermes-pairing-qr/*: deprecation notices
updated to reference the new name
- Historical DEVLOG entries, plan.md build plan, and Python import
paths left untouched on purpose (history and correctness)
## 3. user-docs additive pass for the slash command
user-docs/guide/getting-started.md and README.md now mention
/hermes-relay-pair as the primary "from a Hermes chat session" path
alongside the `hermes pair` CLI. Narrow additive edits only — no
section rewrites.
## Upstream gap discovered
Hermes v0.8.0 has `PluginContext.register_cli_command()` for third-party
plugins but main.py only wires `plugins.memory.discover_plugin_cli_commands()`
into the top-level argparser — the generic `_cli_commands` dict is
populated correctly but never consulted. Result: our `hermes pair` and
`hermes relay` sub-commands register cleanly but aren't callable from
the CLI until an upstream fix lands. The `register_cli_command` calls
in plugin/__init__.py are left in place so they'll start working the
moment main.py is patched.
Practical workaround: use /hermes-relay-pair (this skill) or a shell
shim at ~/.local/bin/hermes-pair that execs `python -m plugin.pair`
(deploy step — not in this commit).
Verified locally:
pip install -e . → hermes-relay 0.5.0
python -c "import plugin.pair"
python -m plugin.pair --help
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Two changes packaged together since the PTY backend landed at the new
plugin/relay/channels/terminal.py location:
1. **PTY terminal backend** — rewrites the terminal channel stub into a
real pty.openpty() + fork + TIOCSCTTY handler with non-blocking master
fd plugged into asyncio.loop.add_reader(). Output batched on ~16ms
frames (or 4 KiB overflow). TIOCSWINSZ resize, graceful SIGHUP →
WNOHANG reap → SIGKILL teardown. Unix-only; import-guarded so the
relay still starts on Windows with a clean "not supported" error on
attach. Adds terminal_shell config field (RELAY_TERMINAL_SHELL env var).
Per-client session cap of 4.
2. **Plugin consolidation (Phase 2 plan step)** — relay_server/ now lives
at plugin/relay/ as the canonical location. relay_server/ is kept as a
thin compat shim so `python -m relay_server` and existing docs keep
working. Adds `hermes relay start` CLI sub-command wired through the
plugin CLI registration system. Bumps plugin version 0.3.0 → 0.4.0,
adds pyyaml to install_requires, libtmux to optional extras.
Deferred (still in the plan, not this commit): libtmux session
persistence, hermes relay status/sessions/kill sub-commands, bridge
channel protocol rewrite (still HTTP↔WS in plugin/android_relay.py on
its own port).
Verified locally:
python -m plugin.relay --help
python -m relay_server --help
python -m py_compile plugin/relay/**/*.py
gradlew :app:compileDebugKotlin (UP-TO-DATE)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Rename "Hermes Relay" -> "Hermes-Relay" across docs, app strings,
scripts, workflows, plugin, and relay server (~57 files)
- Redesign user-docs landing: install section moved above features
via home-hero-after slot (InstallSection.vue), hero image replaced
with phone-framed demo video that crossfades to brand logo after
12s of playback (HeroDemo.vue). Fix dead /guide/getting-started
links in raw HTML hrefs that VitePress base-rewriting skipped.
- Add chat demo to README, docs landing hero, and Getting Started's
Verify Connection section. Re-encode 20MB / 102fps source capture
to 1.95MB / 30fps with extracted poster frame for instant LCP.
- Add smooth auto-scroll for streaming chat with Settings toggle --
follows new tokens at the bottom, pauses when user scrolls up.
- Migrate SettingsScreen clipboard from deprecated LocalClipboardManager
to LocalClipboard suspend API (Compose 1.7+).
- Migrate scripts/screenshots from monolithic .bat to thin wrapper
+ screenshots.py for Play Store screenshot capture.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replaces the standalone bash skill with a plugin-owned CLI sub-command
and adds a curl|bash installer so users can go from zero to connected
without cloning the repo.
Plugin migration:
- plugin/pair.py — pure-Python pairing logic (config reading, LAN IP
detection, QR rendering, text fallback). Cross-platform socket-based
LAN detection replaces Linux-only 'ip route'. Config fallback chain:
config.yaml → ~/.hermes/.env → env vars → defaults
- plugin/cli.py — registers 'hermes pair' via v0.8.0 register_cli API
with --png, --no-qr, --host, --port flags
- plugin/__init__.py — calls ctx.register_cli_command() wrapped in
try/except so the 14 android_* tools still register on v0.7.0
- Pure-Python QR rendering via segno — no qrencode binary dependency
- Text block always shown alongside QR so pairing works inside Hermes
Rich TUI and over limited SSH sessions where QR blocks garble
- Plugin version bumped to 0.3.0; segno>=1.6.0 added to requirements,
setup.py, pyproject.toml
One-line installer:
- install.sh at repo root: curl|bash entry point that clones the plugin,
installs Python deps, and prints next steps. Supports HERMES_HOME and
HERMES_RELAY_BRANCH env overrides. Uses trap cleanup for temp dirs.
Replaces the old plugin/install.sh (its URL comment already pointed
to the root location)
Docs restructure:
- README 'Install' → 'Quick Start' with the one-liner front and center
- Homepage (user-docs/index.md) gets an 'Install in 30 seconds' block
below the feature cards with scoped CSS
- Guide landing page leads with Quick Install
- Getting Started restructured into 3-step Quick Start flow
- CLAUDE.md key files table updated with plugin/cli.py and plugin/pair.py
Deprecation:
- skills/hermes-pairing-qr/SKILL.md gets deprecation frontmatter +
notice in body pointing to 'hermes pair'
- skills/hermes-pairing-qr/hermes-pair prints a deprecation warning
to stderr but stays functional for v0.7.0 users
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>