Files
iris_x_hermes/docs/15-hermes-reference.md
T
ARIA 59acf66c89 M0: toolchain, monorepo scaffold, gateway plugin skeleton, CMP app
- gateway-plugin/: android platform plugin (plugin.yaml + adapter.py
  register(ctx) + no-op AndroidAdapter) + stub modules for M1-M5
- app/: Compose Multiplatform project (shared KMP + androidApp +
  desktopApp) with Gradle wrapper; builds :androidApp:assembleDebug
  and :desktopApp:compileKotlin
- scripts/guard_hermes_agent.sh + pre-commit hook: fail if hermes-agent/
  is staged (read-only reference, never committed)
- .gitignore excludes hermes-agent/; docs/ reference library
2026-08-19 11:27:02 +02:00

7.5 KiB

15 — hermes-agent Source Reference Map

A cheat-sheet of the exact hermes-agent files/lines to read for each integration point. Paths are relative to hermes-agent/ (the read-only reference). This lets a coder jump straight to the right code instead of re-deriving the architecture.

⚠️ Read-only. We install our plugin into ~/.hermes/plugins/android; we never edit these files.

Plugin / platform registration

What Where
How to add a platform (Plugin Path) gateway/platforms/ADDING_A_PLATFORM.md
Canonical plugin-platform example plugins/platforms/irc/adapter.py (esp. register(ctx) at :953, _env_enablement :677, _standalone_send :743, _get_scoped_secret :42)
ntfy plugin example (push-ish) plugins/platforms/ntfy/adapter.py, plugin.yaml
register_platform() (PluginContext) hermes_cli/plugins.py:2774
PlatformEntry dataclass (all fields) gateway/platform_registry.py:63
Platform enum gateway/config.py
Plugin discovery (PluginManager) hermes_cli/plugins.py

Base adapter contract

What Where
BasePlatformAdapter (ABC) gateway/platforms/base.py:2890
MessageEvent (inbound) gateway/platforms/base.py:2300
MessageType gateway/platforms/base.py:2278
SendResult gateway/platforms/base.py:2466
build_source(...) (SessionSource) gateway/platforms/base.py:7047
handle_message(event) gateway/platforms/base.py:5981
send() / edit_message() / delete_message() base.py:3920 / 3976 / 4005
send_typing / stop_typing base.py:4298 / 4307
Media send: send_image/video/document/voice/animation/image_file/multiple_images base.py:4396 / 4632 / 4659 / 4486 / 4415 / 4339 / 7834(tg)
extract_images / extract_media / extract_local_files base.py:4439 / 4884 / 5020
Media cache helpers: cache_image/audio/video/document_from_bytes base.py:854 / 1005 / 1122 / 2126
Inbound media size: get_inbound_media_max_bytes / validate_inbound_media_size base.py:758 / 779
Media delivery security: validate_media_delivery_path + roots/recency/denied base.py:1684 / 1312-1480
Interactive: send_slash_confirm / send_clarify / send_private_notice base.py:4169 / 4204 / 4278
create_handoff_thread base.py:3949
Streaming hooks: supports_draft_streaming / send_draft / render_message_event / format_tool_event base.py:3215 / 3274 / 3316 / 3337
Scoped secret read pattern (profile-safe) plugins/platforms/irc/adapter.py:42
Scoped lock (profile-safe bind) gateway/status.py (acquire_scoped_lock)

Streaming (legacy callback path — what the main gateway uses)

What Where
GatewayStreamConsumer (the sink) gateway/stream_consumer.py:156
on_delta / on_commentary / on_segment_break / finish stream_consumer.py:611 / 518 / 514 / 623
Consumer run() loop (edit cadence) stream_consumer.py:781
Structured events (ACP-only, NOT main gateway) gateway/stream_events.py, gateway/stream_dispatch.py:40
Callback wiring (agent → consumer) gateway/run.py:5642-5704 (tool_progress_callback, stream_delta_callback, interim_assistant_callback, status_callback, event_callback)
send_progress_messages (tool progress → send) gateway/run.py:4603
Progress metadata gateway/run.py:28089

Reasoning display

What Where
Reasoning prepended to final response gateway/run.py:20089-20127
show_reasoning / reasoning_style defaults + per-platform gateway/display_config.py:33-181
resolve_display_setting() gateway/display_config.py:187
last_reasoning in agent result gateway/run.py:6471
Think-tag filtering in consumer stream_consumer.py:175-185, 627+

Display settings (tool progress, interim, streaming, etc.)

What Where
All overrideable display keys + defaults gateway/display_config.py:33 (tool_progress, show_reasoning, reasoning_style, tool_preview_length, streaming, interim_assistant_messages, long_running_notifications, cleanup_progress, live_status)
Per-platform tiers gateway/display_config.py:81-181

Slash commands

What Where
COMMAND_REGISTRY / CommandDef (all commands) hermes_cli/commands.py:144+
GATEWAY_KNOWN_COMMANDS / is_gateway_known_command / resolve_command hermes_cli/commands.py
Gateway command dispatch (alias, access, hooks) gateway/run.py:16974-17099
send_model_picker / send_choice_picker (Telegram ref) plugins/platforms/telegram/adapter.py:6350 / 6424
Picker invocation from gateway gateway/slash_commands.py:1859, 2157, 3622-3657
Button-callback id conventions (cl:, appr:, sc:) gateway/platforms/ADDING_A_PLATFORM.md (Interactive UX)

Cron delivery

What Where
Resolve a delivery target (platform:chat_id[:thread_id]) cron/scheduler.py:2148 (_resolve_single_delivery_target)
_resolve_delivery_targets / routing tokens (all) cron/scheduler.py:2296 / 2278
cron_deliver_env_var handling (home channel) cron/scheduler.py:1903+
deliver param normalization cron/scheduler.py:2251
send_message tool target resolution (resolve_send_target, prepare_send_message_platforms) tools/send_message_tool.py
parse_target_ref_fn usage tools/send_message_tool.py (_parse_target_ref)
Cron mirror delivery (default off) cron/scheduler.py:1520
cronjob tool schema (deliver description) tools/cronjob_tools.py

Search (FTS5)

What Where
Session store (SQLite + FTS5) hermes_state.py
Search implementation hermes_state_search.py
Schema hermes_state_schema.py

Config / env / profiles

What Where
get_hermes_home() / display_hermes_home() (profile-safe paths) hermes_constants.py
DEFAULT_CONFIG / OPTIONAL_ENV_VARS hermes_cli/config.py
Gateway config load (load_gateway_config, _apply_env_overrides) gateway/config.py
Profile override (_apply_profile_override) hermes_cli/main.py
Secret scope (multiplex fail-closed) agent/secret_scope.py
PII redaction agent/redact.py

Dependencies (confirm zero new deps)

What Where
websockets==15.0.1 (core) pyproject.toml:111
httpx[socks]==0.28.1 (core) pyproject.toml:44
aiohttp (messaging extra, NOT core) pyproject.toml:185
Dependency pinning policy pyproject.toml:19-39, root AGENTS.md

Testing

What Where
Hermetic test runner (use this, not bare pytest) scripts/run_tests.sh
_isolate_hermes_home fixture tests/conftest.py
Example platform tests tests/gateway/test_*.py (e.g. test_google_chat.py, test_line_plugin.py)
Stream-event tests (dispatcher) tests/gateway/test_stream_events.py

Existing desktop app (for reference only — we do NOT reuse its backend)

What Where
Electron desktop app apps/desktop/ (its own AGENTS.md, DESIGN.md)
Shared JSON-RPC WS client (tui_gateway protocol) apps/shared/src/json-rpc-gateway.ts
tui_gateway WS transport (mobile-client-ready) tui_gateway/ws.py
tui_gateway method catalog tui_gateway/server.py

Note: the existing desktop app talks to the tui_gateway backend (hermes serve), a different process from the messaging gateway. Our apps talk to the messaging gateway via our own plugin + protocol. We borrow naming conventions only.