feat(hermes): add Rocket.Chat alarms for incident escalation (opensre hermes watch) - #4643
feat(hermes): add Rocket.Chat alarms for incident escalation (opensre hermes watch)#4643mdiniz97 wants to merge 5 commits into
Conversation
TelegramSink.__init__ and make_telegram_sink hardcoded integrations.telegram.alarms.AlarmDispatcher as the dispatcher type, even though the sink only ever calls dispatch(name, message) -> bool on it. Add a local AlarmDispatcherPort Protocol (matching the existing tools.system.watch_dog.monitor.AlarmDispatcherPort pattern) so a RocketChatAlarmDispatcher can be passed in without a type error, with no behavior change — all 39 existing tests pass unchanged.
--provider telegram|rocketchat (default telegram, unchanged behavior) on hermes_watch, branching dispatcher construction the same way the watchdog CLI does. --chat-id help text and the startup echo line generalized to name both providers.
…laim hermes.mdx: --provider flag, ROCKETCHAT_* env vars, intro/note mention both providers. rocketchat.mdx: new 'Hermes incident escalation' section; also corrects the watchdog section's inaccurate claim that alarms follow the scheduled-delivery (cron/digest) rule of requiring token credentials only — watchdog/Hermes alarms actually accept a webhook-only configuration too, unlike cron/digest which need an explicit --chat-id destination a webhook's fixed destination can't honor.
Drifted after Tracer-Cloud#4217 added Rocket.Chat watchdog alarms — the repo map still said 'Telegram alarm dispatch' only.
Greptile code reviewThis repo uses Greptile for automated review. Before merge, aim for Confidence Score: 5/5 with zero unresolved review threads — see CONTRIBUTING.md. Run a review — add a PR comment with: Give it ~5-10 minutes (sometimes longer) for results, then fix feedback and re-trigger until you reach Confidence Score: 5/5. Optional: automate with the greploop skill. |
Greptile SummaryAdds Rocket.Chat as a Hermes incident-delivery provider while preserving Telegram as the default.
Confidence Score: 5/5The PR appears safe to merge. The previously reported provider-specific documentation wording has been corrected, and no blocking failure remains.
|
| Filename | Overview |
|---|---|
| integrations/hermes/sinks.py | Replaces the Telegram-specific dispatcher annotation with a minimal structural dispatch protocol. |
| surfaces/cli/commands/hermes.py | Adds provider selection and constructs the corresponding Telegram or Rocket.Chat dispatcher. |
| tests/cli/test_hermes_command.py | Covers help output, default and Rocket.Chat dispatcher selection, destination forwarding, and invalid-provider rejection. |
| docs/hermes.mdx | Documents both delivery providers and consistently applies provider-neutral wording to deduplication and investigation behavior. |
| docs/messaging/rocketchat.mdx | Documents Rocket.Chat delivery for Hermes escalation and clarifies supported credential modes. |
Reviews (2): Last reviewed commit: "docs(hermes): make correlator/investigat..." | Re-trigger Greptile
There was a problem hiding this comment.
Pull request overview
Adds Rocket.Chat as an alarm delivery provider for opensre hermes watch, aligning Hermes incident escalation with the watchdog’s provider model by reusing the existing RocketChatAlarmDispatcher and loosening Hermes sink typing via a local dispatcher Protocol.
Changes:
- Add
--provider telegram|rocketchattoopensre hermes watchand branch dispatcher construction accordingly. - Introduce a minimal
AlarmDispatcherPortProtocol in Hermes sinks so both Telegram and Rocket.Chat dispatchers type-check without coupling to a concrete provider. - Update docs and CLI tests to cover the new provider flag and Rocket.Chat configuration.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/cli/test_hermes_command.py | Adds CLI wiring tests for --provider, defaulting behavior, and --chat-id → Rocket.Chat channel override. |
| surfaces/cli/commands/hermes.py | Adds --provider flag and constructs the correct dispatcher/credentials loader based on provider. |
| integrations/hermes/sinks.py | Replaces concrete AlarmDispatcher typing with a local Protocol (AlarmDispatcherPort). |
| docs/messaging/rocketchat.mdx | Documents Hermes incident escalation support for Rocket.Chat alongside watchdog alarms. |
| docs/hermes.mdx | Updates Hermes docs to mention Rocket.Chat and the new --provider flag / env vars. |
| AGENTS.md | Updates repo map wording for watchdog alarms to include Rocket.Chat provider support. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| Loads the selected provider's credentials from the environment, | ||
| constructs a :class:`HermesAgent` wired to a :class:`TelegramSink` | ||
| (delivering via Telegram or Rocket.Chat depending on ``--provider``), | ||
| then waits for ``SIGINT``/``SIGTERM`` before shutting the agent down | ||
| cleanly. |
| | `--chat-id ID` | Overrides the provider's default destination (`TELEGRAM_DEFAULT_CHAT_ID` or `ROCKETCHAT_DEFAULT_CHANNEL`) for this run | | ||
| | `--cooldown-seconds N` | Per-fingerprint cooldown before the same incident is sent again (default `300`) | | ||
| | `--from-start` | Replay the file from the beginning, then tail | | ||
| | `--investigate` / `--no-investigate` | Run an OpenSRE RCA for `HIGH` / `CRITICAL` incidents and append the summary to Telegram | |
Review flagged that the command reference now covers both providers, but the adjacent dedup and RCA-trigger sentences still said 'Telegram' specifically, which would read as those features not applying to Rocket.Chat deliveries.
|
@greptile review |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 6 out of 6 changed files in this pull request and generated no new comments.
Suppressed comments (3)
surfaces/cli/commands/hermes.py:46
- The Click group docstring still says Hermes routes incidents to Telegram, but this command now supports Rocket.Chat via --provider. This string is used in
opensre hermes --help, so it should be provider-neutral.
from integrations.telegram.alarms import AlarmDispatcher
from integrations.telegram.credentials import load_credentials_from_env
from tools.investigation.capability import run_investigation
@click.group(name="hermes", invoke_without_command=True)
@click.pass_context
def hermes_command(ctx: click.Context) -> None:
"""Live-tail Hermes logs and route detected incidents to Telegram."""
integrations/hermes/sinks.py:179
TelegramSinknow accepts a provider-agnosticAlarmDispatcherPort, so it can deliver via Rocket.Chat too. However, the exception log line still saystelegram sink failed, which will be misleading when--provider rocketchatis in use; similarly, nearby docstrings still describe the sink as Telegram-specific. Consider making the log message/docstrings provider-neutral (e.g., "Hermes incident sink").
)
def __init__(
self,
dispatcher: AlarmDispatcherPort,
*,
investigation_bridge: InvestigationBridge | None = None,
surfaces/cli/commands/hermes.py:78
- The
--from-starthelp text still hardcodes Telegram, but the watcher can now deliver via Rocket.Chat too. Update the wording to avoid provider-specific copy in--help.
@click.option(
"--chat-id",
"chat_id",
type=str,
default=None,
help=(
"Chat/channel to deliver incidents to. Overrides the provider's "
"default (TELEGRAM_DEFAULT_CHAT_ID or ROCKETCHAT_DEFAULT_CHANNEL) "
"when both are set."
),
Fixes #4215
Describe the changes you have made in this PR -
Adds Rocket.Chat as an alarm delivery provider for
opensre hermes watch(live-tail Hermes error logs, classify incidents, escalate) — the Hermes counterpart to the watchdog Rocket.Chat alarms in #4217, reusingRocketChatAlarmDispatcherrather than reimplementing it.integrations/hermes/sinks.py—TelegramSink.__init__andmake_telegram_sinkhardcodedintegrations.telegram.alarms.AlarmDispatcheras the dispatcher type, even though the sink only ever callsdispatch(name, message) -> bool. Added a localAlarmDispatcherPortProtocol (matching the existingtools.system.watch_dog.monitor.AlarmDispatcherPortpattern) so aRocketChatAlarmDispatchersatisfies the type without a mypy error. No behavior change — all 39 pre-existing tests pass unchanged.surfaces/cli/commands/hermes.py—--provider telegram|rocketchatonopensre hermes watch(defaulttelegram), branching dispatcher construction the same way the watchdog CLI does;--chat-idhelp text and the startup echo line generalized to name both providers.TelegramSink's incident message is already plain text — safe to send to either provider as-is.docs/hermes.mdxgets the--providerflag,ROCKETCHAT_*env vars, and provider-neutral intro copy;docs/messaging/rocketchat.mdxgets a new "Hermes incident escalation" section. Also fixed a doc-accuracy issue found while editing the adjacent watchdog section: it claimed watchdog/Hermes alarms follow the same token-only rule as scheduled deliveries (cron/Sentry digest) — they don't; both accept a webhook-only configuration too, since neither carries a fixed--chat-idrequirement the way cron/digest do. Also fixed a staleAGENTS.mdrepo-map line (drifted after feat(rocketchat): add Rocket.Chat alarms for the watchdog (/watch, opensre watchdog) #4217, still said "Telegram alarm dispatch" only).Testing —
make lint,make format-check,make typecheck,make check-layers-strictall pass. 190 tests acrosstests/hermes/andtests/cli/test_hermes_command.pypass (10 new: provider flag help text, default-telegram dispatcher construction, rocketchat dispatcher construction,--chat-id→channel_overridepassthrough, invalid-provider rejection), zero regressions. Verified end-to-end against a real Rocket.Chat workspace: constructed the exact CLI code path (TelegramSink+RocketChatAlarmDispatcherfrom realload_credentials_from_env()output) and dispatched a syntheticHIGHincident — delivered without exception (screenshot below).Demo/Screenshot for feature changes and bug fixes -
Code Understanding and AI Usage
Did you use AI assistance (ChatGPT, Claude, Copilot, etc.) to write any part of this code?
If you used AI assistance:
Explain your implementation approach:
This closes out the alarm-delivery parity work started with the watchdog (#4214/#4217): both features hardcoded Telegram via the same
AlarmDispatcherclass, and #4217 already built the reusableRocketChatAlarmDispatcher+ cooldown primitive specifically so this PR could stay small. The only real design decision here was how to makeTelegramSinkaccept either dispatcher without weakening its typing — a local structural Protocol (mirroring an existing pattern already in the codebase) does that with a four-line change and zero behavior risk, rather than widening the concrete type or reaching forAny.I checked whether the watchdog's HTML-formatting bug (caught during that PR's manual demo) applies here too before assuming it doesn't — it doesn't, because
TelegramSink's message builder never used<b>/<code>tags in the first place, so Rocket.Chat renders the same plain-text body correctly with no formatter split needed.Checklist before requesting a review