You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
fix(relay): keep interaction and message identifiers distinct - #130309
Keeps Discord interaction identifiers separate from actual Discord message identifiers in the relay lane. Component interactions use the attached message as the event and source message target, while application commands do not invent a message ID. The interaction ID remains available in event metadata for acknowledgement and tracing. Relay wire decoding also normalizes a root-only message ID into the source.
AI code review — automated review for reference; please use your judgment.
Reviewed at head 5af4e398. The identity split is right and I would not ask you to roll it back. I reproduced all three of #130306's cases on base first: base set source.message_id = payload["id"] (the interaction id) on every shape, so reaction acks and HERMES_SESSION_MESSAGE_ID read an interaction id as a message target. The head fixes all three — slash/modal → None, component → the real message.id, root-only frame → source and event agree.
To finish: nulling source.message_id on slash re-renders the pinned prompt
source.message_id's presence is an input to _ephemeral_change_key (gateway/run_agent_cache.py) and decides whether _discord_platform_notes (gateway/session.py) emits the - Triggering message: provided per-turn… line. Against a relayed text message in the same chat, on base slash, component and modal all land on one pin key with that line present; on head component stays on the text key but slash and modal drop to another, with it absent.
Text → slash → text therefore re-keys the pin twice: turn 2 re-renders the system prompt, turn 3 re-renders it back. AGENTS.md makes prompt caching the repo's first invariant, and rewriting already-sent system bytes mid-conversation is what it warns against. This is the trap #130306 flagged — a split "cannot simply blank source.message_id for slash commands (the regression teknium1 measured on #129032)."
So you don't over-correct: it is not relay-only. Native Discord already behaves this way — _build_slash_event (plugins/platforms/discord/adapter.py:4724) never passes message_id to build_source. The head aligns relay with the native lane rather than inventing a new bad state, so keep the split and finish it: render the guidance from a lane-independent presence bit rather than source.message_id's emptiness, and switch that component of _ephemeral_change_key to match.
Minor: the ws_transport change has no test
Reverting that line — undoing this file's whole behaviour change — leaves all 37 tests across the ws-transport, passthrough, dedupe and wire-identity relay tests green, so the root-only-frame normalisation listed as one of your three changes is unasserted. The issue's regressions list asks for a source.message_id is not None assertion on that frame.
@Enough1122 Addressed in separate fix/test commits 53cd7a165dfb3698ecb1ee9001fd4d87cfc4b0d5 and e2896f8ca72e77bed5f2445ae1866a1f94bc5026.
The interaction/message identity split is unchanged: slash and unattached modal turns have no message target, components use the attached message ID, and the interaction ID remains in metadata only.
_discord_platform_notes now renders triggering-message guidance from the existing Discord-tools capability gate, independent of the current source's message-ID presence. Its wording explicitly permits an absent per-turn target and forbids substituting an interaction ID. _ephemeral_change_key retains the same capability gate but no longer includes message-ID presence. This applies equally to native and relayed Discord, without adding a synthetic message ID or a relay-only flag.
Behavioral coverage:
Real relay text/interaction converters exercise text → slash/component/modal → text with Discord tools enabled. They assert identical rendered guidance and keys, reuse of the same pinned string object, and exactly one renderer call. The session-env and incoming-user-message consumers receive the actual message target or no target, never the interaction ID or the previous turn's target.
The native _build_slash_event path also exercises text → slash → text prompt continuity. Additional tests cover both tool-gate states and verify that an actual capability change still changes the guidance and key.
The real in-process WebSocket transport now receives both root-only and root-plus-nested message-ID frames; both assert source.message_id is not None and equality with event.message_id.
Evidence: before the prompt fix, the new relay slash/modal and native slash continuity cases failed (3 failed, 3 passed). Temporarily reverting only the root-ID normalization made the root-only socket case fail at the explicit non-null assertion while the nested case passed (1 failed, 1 passed); that mutation was restored before the final runs.
Using the shared venv's python3 -m pytest, final results:
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
comp/gatewayGateway runner, session dispatch, deliveryP2Medium — degraded but workaround existsplatform/discordDiscord bot adaptersweeper:risk-session-stateSweeper risk: may lose/corrupt/mis-associate session or context statetype/bugSomething isn't working
3 participants
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
Keeps Discord interaction identifiers separate from actual Discord message identifiers in the relay lane. Component interactions use the attached message as the event and source message target, while application commands do not invent a message ID. The interaction ID remains available in event metadata for acknowledgement and tracing. Relay wire decoding also normalizes a root-only message ID into the source.
Related Issue
Fixes #130306
Type of Change
Changes Made
gateway/relay/adapter.py: map attached Discord component messages to message targets and preserve interaction IDs separately.gateway/relay/ws_transport.py: normalize root and nested relay message IDs.tests/gateway/relay/test_relay_interactive.py: cover component and application-command identity behavior.How to Test
/Users/liyuan/github/ai/hermes-agent/venv/bin/python3 -m pytest tests/gateway/relay/test_relay_interactive.py tests/gateway/relay/test_ws_transport.py -q28 passed, 1 warning in 10.67sChecklist