Skip to content

fix(config+telegram): prevent duplicate config key and duplicate message delivery (#71047) - #71076

Open
webtecnica wants to merge 2 commits into
NousResearch:mainfrom
webtecnica:fix/config-set-duplicate-telegram-resend-reply
Open

fix(config+telegram): prevent duplicate config key and duplicate message delivery (#71047)#71076
webtecnica wants to merge 2 commits into
NousResearch:mainfrom
webtecnica:fix/config-set-duplicate-telegram-resend-reply

Conversation

@webtecnica

Copy link
Copy Markdown
Contributor

Fixes #71047

Problem A — hermes config set duplicates top-level config key

Running hermes config set platforms.telegram.streaming false appended a new platforms: block at EOF instead of editing the existing one, because set_config_value navigated on the raw user config (stripped of DEFAULT_CONFIG-only subtrees like platforms). The gateway reads the first block, so config set had no runtime effect — a silent, confusing failure.

Fix: Use load_config() (merged with DEFAULT_CONFIG) as the navigation base for _set_nested, so keys present only in defaults are found and mutated in-place. Write back only the raw user-set keys via atomic_yaml_write as before, preserving the contract of not dumping defaults.

Problem B — Streaming + reply_to_mode='first' duplicates final message

When streaming: true and reply_to_mode: 'first', the streamed preview is sent as a reply to the user. If the final edit fails (flood control), the fallback paths (_send_fallback_final and _send_empty_fallback_final) sent the completed answer as a fresh non-reply message, producing two visible messages — the reply-preview and the plain resend.

Fix: Preserve the reply anchor by passing reply_to=self._initial_reply_to_id on every fallback/continuation adapter.send() call, matching the first-send path at line 2224 that already passes it.

Files changed

File Change
hermes_cli/config.py Use load_config() as navigation base in set_config_value; write back raw config
gateway/stream_consumer.py Pass reply_to=self._initial_reply_to_id on fallback send + continuation chunk send
tests/gateway/test_telegram_final_delivery.py Assert reply_to kwarg in adapter.send calls

Tests

  • tests/hermes_cli/test_set_config_value.py: 91/91 passed
  • tests/hermes_cli/test_config.py: 186/186 passed
  • tests/gateway/test_telegram_final_delivery.py: 10/10 passed
  • tests/gateway/: 301 passed, 1 pre-existing failure (Honcho cache busting), 1 skipped

@alt-glitch alt-glitch added type/bug Something isn't working comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/cli CLI entry point, hermes_cli/, setup wizard comp/gateway Gateway runner, session dispatch, delivery comp/plugins Plugin system and bundled plugins area/config Config system, migrations, profiles platform/telegram Telegram bot adapter platform/matrix Matrix adapter (E2EE) P2 Medium — degraded but workaround exists sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades labels Jul 24, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

Related: #71047 (config and Telegram report), #71077 (Codex replay pruning), and #71073 (Matrix encryption-state fallback). This omnibus combines three independently scoped fixes; the latter two have open focused PRs, so it is related rather than a duplicate.

…on (NousResearch#71067)

_CryptoStateStore.get_encryption_info only consulted mautrix's in-memory
MemoryStateStore, which has no record of m.room.encryption for rooms the
bot joined in the past (the raw-sync path never feeds those state events
through set_encryption_info). On a fresh crypto store this returns None
for all previously-joined rooms, so OlmMachine reports them as unencrypted,
never tracks peer devices, and silently drops all inbound messages.

Fix: pass the mautrix Client into _CryptoStateStore so get_encryption_info
can fall back to a live GET /_matrix/client/v3/rooms/{room_id}/state/
m.room.encryption query when the in-memory store returns None. The result
is cached back via set_encryption_info so subsequent lookups (and
OlmMachine device tracking) hit the fast path.
…age delivery (NousResearch#71047)

Problem A: `hermes config set platforms.telegram.streaming false`
appended a new top-level `platforms:` block instead of editing the
existing one, because set_config_value navigated on the raw user config
which lacks DEFAULT_CONFIG-only subtrees (platforms). The gateway reads
the first block, so the change had no runtime effect.

Fix: use load_config() (merged with defaults) as the navigation base
so keys present only in defaults are found by _set_nested.  Write back
only the raw user-set keys via atomic_yaml_write as before.

Problem B: When streaming + reply_to_mode='first' and the final edit
fails (flood control), the fallback resend (via _send_fallback_final
and _send_empty_fallback_final) sent the completed answer as a fresh
non-reply message, producing two visible messages — the streamed reply
preview and the plain resend.

Fix: preserve the reply anchor by passing
reply_to=self._initial_reply_to_id on every fallback- and continuation-
send path, matching the first-send path at line 2224 that already passes
it.
@teknium1

Copy link
Copy Markdown
Contributor

Thank you for tracing both delivery and config behavior. The Telegram fallback direction is useful, but the PR needs a focused rework before it can be salvaged.

Problems

Suggested changes

  • Canonicalize or reject the noncanonical config path and add a raw-config-to-runtime regression for display.platforms.telegram.streaming.
  • Test fallback sends with a non-null initial reply anchor.
  • Split the unrelated Matrix and Codex work from this salvage.

This is an automated hermes-sweeper review.

@teknium1 teknium1 added sweeper:risk-caching Sweeper risk: may break/degrade prompt caching or cache-key stability (invariant) sweeper:blast-broad Sweeper blast radius: broad — a core path most sessions hit labels Jul 30, 2026
@zoser69

zoser69 commented Aug 4, 2026

Copy link
Copy Markdown

Heads up: I opened #78111 as a focused fix for only Problem A of #71047 (the config set mis-targeted key), per the triage recommendation to keep that fix narrow and drop the unrelated Matrix/Codex changes.

#78111 redirects platforms.<name>.<display_setting>display.platforms.<name>.<setting> (the path gateway/display_config.py::resolve_display_setting actually reads), and only for known display settings, leaving connection keys (token, extra, …) untouched. It includes a real-path test against a temp HERMES_HOME.

If the maintainers prefer to consolidate on a single PR, #78111's set_config_value redirect could be cherry-picked into this one (or vice versa). Either way, Problem A should land against the confirmed display.platforms hierarchy rather than writing to the top-level platforms block.

@alt-glitch alt-glitch added needs-decision Awaiting maintainer decision before any implementation comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/plugins Plugin system and bundled plugins sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state and removed comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/plugins Plugin system and bundled plugins platform/matrix Matrix adapter (E2EE) sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state sweeper:risk-caching Sweeper risk: may break/degrade prompt caching or cache-key stability (invariant) labels Aug 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/config Config system, migrations, profiles comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/cli CLI entry point, hermes_cli/, setup wizard comp/gateway Gateway runner, session dispatch, delivery comp/plugins Plugin system and bundled plugins needs-decision Awaiting maintainer decision before any implementation P2 Medium — degraded but workaround exists platform/telegram Telegram bot adapter sweeper:blast-broad Sweeper blast radius: broad — a core path most sessions hit sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: config set duplicates top-level key + Telegram streaming+reply_to_mode='first' duplicates final message

4 participants