hermes-agent's native installer imports the plugin directory as
hermes_plugins.hermes_relay — no top-level 'plugin' package exists there,
so every absolute 'from plugin.X' import crashed 'hermes relay start'
with ModuleNotFoundError: No module named 'plugin'.
- Convert all runtime absolute plugin.* imports to package-relative form
(relay voice/realtime chain, tailscale CLI, pair, enhancements, tools).
- android_tool's direct-script fallback now imports the sibling module
bare instead of via 'plugin.tools.'.
- dashboard/plugin_api.py is exec'd standalone by the dashboard web
server (spec_from_file_location, no parent package), so relative
imports can't work there: add a _plugin_module() bootstrap that
imports through the real parent package when one exists, and
otherwise synthesizes it (bare ModuleType with __path__ at the plugin
dir under a stable sys.modules alias) without exec'ing
plugin/__init__.py side effects.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds the Phase 3 Tier 4 `android_navigate(intent, max_iterations=5)`
tool — a close-the-loop wrapper around the Wave 1 bridge HTTP routes
that takes a natural-language intent, screenshots the phone, asks a
vision model for the next action, executes it, and repeats until the
model emits `done` or the iteration cap is hit. Default cap 5,
hard-clamped to 20.
Hard plan constraint honoured: never continuous capture. Exactly one
/screenshot per iteration, only when the tool is invoked.
- plugin/tools/android_navigate.py — main loop, action dispatch, and
registry registration. Shares _bridge_url()/_auth_headers() with
android_tool.py so pairing config stays single-sourced.
- plugin/tools/android_navigate_prompt.py — prompt template + response
parser. Kept separate so the parser tests don't need requests.
- plugin/tests/test_android_navigate.py — 35 stdlib unittest cases
covering every valid action, every malformed-input branch, and the
full loop (success / iteration cap / screenshot failure / parse
error / action failure / llm_gap envelope / empty intent / clamping
/ schema sanity). Runs via `python -m unittest` without pulling in
the conftest.py `responses` dependency.
LLM integration is a known gap documented in the module docstring: the
plan doesn't pick a vision provider, the plugin has no published LLM
client surface for tools, and the gateway's run loop is not re-entrant.
The loop defines a `call_vision_model` injection point — tests patch
it, production either sets HERMES_NAVIGATE_STUB_REPLY for smoke runs
or swaps _default_vision_model for a real Anthropic/OpenAI client.
Until that's wired, live calls return a clean {status: error, reason:
llm_gap} envelope instead of silently faking actions or crashing.
DEVLOG + CLAUDE.md Key Files rows updated for the three new files.