fix(gateway): prevent duplicate messages on no-message-id platforms#6853
Closed
KUSH42 wants to merge 2 commits into
Closed
fix(gateway): prevent duplicate messages on no-message-id platforms#6853KUSH42 wants to merge 2 commits into
KUSH42 wants to merge 2 commits into
Conversation
Platforms that don't return a message_id after the first send (Signal, GitHub webhooks) were causing GatewayStreamConsumer to re-enter the "first send" path on every tool boundary, posting one platform message per tool call (observed as 155 PR comments on a single response). Fix: treat _message_id == "__no_edit__" as a sentinel meaning "platform accepted the send but cannot be edited". When a tool boundary arrives in that state, skip the message_id/accumulated/last_sent_text reset so all continuation text is delivered once via _send_fallback_final rather than re-posted per segment. Also make prompt_toolkit imports in hermes_cli/commands.py optional so gateway and test environments that lack the package can still import resolve_command, gateway_help_lines, and COMMAND_REGISTRY.
- test_background_autocompletes: pytest.importorskip("prompt_toolkit")
so the test skips gracefully where the CLI dep is absent
- test_run_agent_progress_stays_in_originating_topic: update stale emoji
💻 → ⚙️ to match get_tool_emoji("terminal", default="⚙️") in run.py
- test_internal_event_bypass{_authorization,_pairing}: mock
_handle_message_with_agent to raise immediately; avoids the 300s
run_in_executor hang that caused the tests to time out
Contributor
|
Merged via PR #7123. Both commits were cherry-picked onto current main with your authorship preserved. Great catch on the duplicate message bug — thanks! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
On platforms that accept a message send but return no `message_id` (Signal, GitHub webhooks with `github_comment` delivery), `GatewayStreamConsumer` was re-entering the "first send" path on every tool-call boundary (`on_delta(None)`). Each segment reset cleared `_message_id`, `_accumulated`, and `_last_sent_text`, so the next text chunk triggered a fresh `adapter.send()` — posting one platform message per tool call. This is what caused 155 PR comments under a single response.
The fix: when `_message_id == "no_edit"` (the sentinel set by `_send_or_edit` when a platform returns no ID), skip the segment-break reset. State is preserved so all continuation text accumulates and is delivered once via `_send_fallback_final` at stream end.
Platforms with real message IDs (Telegram, Discord, Slack) are unaffected — their `_message_id` is never `"no_edit"` so the reset still fires normally.
Also makes `prompt_toolkit` imports in `hermes_cli/commands.py` optional so gateway and test environments that lack the package can import `resolve_command`, `gateway_help_lines`, and `COMMAND_REGISTRY` without error.
Related Issue
N/A
Type of Change
Changes Made
How to Test
Or run the new test directly:
```
pytest tests/gateway/test_stream_consumer.py::TestSegmentBreakOnToolBoundary::test_no_message_id_segment_breaks_do_not_resend -v
```
Checklist
Code
fix(scope):,feat(scope):, etc.)Documentation & Housekeeping
Screenshots / Logs
N/A