136 lines
7.5 KiB
Markdown
136 lines
7.5 KiB
Markdown
# 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/iris`; 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. |