Skip to content

fix(gateway): voice turns drop the /voice join message id and name uncached speakers - #133598

Merged
unsupportedpastels merged 2 commits into
NousResearch:mainfrom
unsupportedpastels:fix/voice-turn-trigger-and-uncached-name
Oct 6, 2026
Merged

unsupportedpastels merged 2 commits into
NousResearch:mainfrom
unsupportedpastels:fix/voice-turn-trigger-and-uncached-name

Conversation

@unsupportedpastels

@unsupportedpastels unsupportedpastels commented Oct 6, 2026 •

Copy link
Copy Markdown
Collaborator

What does this PR do?

Voice-channel turns rebuild their SessionSource from the copy bound at /voice join. #133463 made that rebuilt source carry the speaker's display name and the channel's current labels, so typing and talking now render the same pinned prompt. Two problems from #127944 were not part of that fix, and this PR carries teknium's commits for both from that PR onto current main:

  • Voice turns inherit the /voice join message id. A typed /voice join binds that message's source, message_id included, and every later voice turn kept it. run.py exports source.message_id as HERMES_SESSION_MESSAGE_ID, the turn's triggering message. On Discord, the background-process tool stores it as watcher_message_id, the reply anchor for completion notices, so a process started from a voice turn recorded the old /voice join message as its trigger. The cron and Kanban tools read it only for Slack and Telegram, so they aren't affected here. Voice turns now clear it. This no longer affects the prompt cache, because fix(discord): slash, /thread and voice turns carry a message turn's prompt inputs #133463 made the triggering-message note static.
  • A speaker outside the member cache renders as a bare id. guild.get_member() is cache-only. A speaker it misses who isn't the /voice join invoker rendered as **User:** "<id>", so their turns still re-rendered the pinned prompt. The rebuilt source now falls back to the cached user's display name (global name or username) before the id.
Speaker main This PR
Member cached display name display name (unchanged)
/voice join invoker, member not cached name bound at join name bound at join (unchanged)
Other speaker, member not cached, user cached bare id global name / username
Cached nowhere bare id bare id (unchanged)
Any voice turn after a typed /voice join join message id as message_id no message_id

Adapted onto main. #127944 looked the name up through a new DiscordAdapter.voice_member_display_name. On main, #133463 resolves it inline in _voice_input_source, so both commits are rebuilt there, with conflict notes in the commit messages:

  • The cached-user fallback is a small _cached_user_display_name helper.
  • It runs after the joiner's bound name, not before as in fix(gateway): voice-channel turns render the same session prompt as text turns #127944. The bound name is the server nickname the joiner's typed messages carry; their cached global name would differ from it.
  • The tests moved into tests/gateway/test_discord_turn_prompt_parity.py. test_voice_session_prompt.py isn't on main.

Known gap (out of scope). A speaker who has a server nickname, but is in neither the member cache nor the join binding, now gets their global name. That beats an id, but still differs from the nickname their typed messages carry. Matching it exactly would need a member fetch over the API on the voice path.

Related Issue

No issue. These are the remaining parts of #127944, whose name and triggering-note fixes landed in #133463.

Credit: both commits are @teknium1's review fixes on #127944, cherry-picked with -x and authorship kept. The underlying work and diagnosis are @matttmcfarland's #127944, and @andrexibiza independently reproduced the uncached-speaker fallback (F3 on that PR).

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✨ New feature (non-breaking change that adds functionality)
  • 🔒 Security fix (undisclosed vulnerabilities in SECURITY.md §3.1 scope go to Security Advisories first, not a public PR)
  • 📝 Documentation update
  • ✅ Tests (adding or improving test coverage)
  • ♻️ Refactor (no behavior change)
  • 🎯 New skill (bundled or hub)

Changes Made

  • gateway/run_voice.py: _voice_input_source clears the bound message_id, and both its branches fall back to _cached_user_display_name (new) before the bare id.
  • tests/gateway/test_discord_turn_prompt_parity.py: the voice parity test binds a typed /voice join id and asserts that the voice turn doesn't carry it. The uncached case covers another speaker cached only as a user, one cached nowhere, and no binding at all. The joiner keeps their bound nickname over their cached global name.

How to Test

  1. pytest tests/gateway/test_discord_turn_prompt_parity.py -q: 12 passed.
  2. With main's run_voice.py, 4 of them fail: the message-id assertion in every voice case. With only the first commit's run_voice.py, 1 fails: the uncached-speaker case.
  3. The Discord, voice, session, cron, prompt-freeze and pin-wiring test files (113 files) give 822 passed, 0 failed, 2 skipped. ruff passes, and ty reports the same 34 diagnostics on the two files as on main.

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits (fix(scope):, feat(scope):, etc.)
  • I searched for existing PRs to make sure this isn't a duplicate (only fix(gateway): voice-channel turns render the same session prompt as text turns #127944 has these commits)
  • My PR contains only changes related to this fix/feature (no unrelated commits)
  • I've run pytest tests/ -q and all tests pass: I ran the 113 related files above, not the full suite
  • python scripts/check passes (the blocking lint and code-health checks CI runs)
  • I've added tests for my changes (required for bug fixes, strongly encouraged for features)
  • I've tested on my platform: unit tests only, on Linux; no live Discord voice session

Documentation & Housekeeping

  • I've updated relevant documentation (README, docs/, docstrings) — or N/A
  • I've updated cli-config.yaml.example if I added/changed config keys — or N/A
  • I've updated CONTRIBUTING.md or AGENTS.md if I changed architecture or workflows — or N/A
  • I've considered cross-platform impact (Windows, macOS) per the compatibility guide — or N/A
  • I've updated tool descriptions/schemas if I changed tool behavior — or N/A

Infographic

voice-turn-source-fixes

A typed `/voice join` binds that message's source, message_id included, and
`_voice_input_source` rebuilt every voice turn from it. The join message is
not the voice turn's trigger: run.py exports source.message_id as
HERMES_SESSION_MESSAGE_ID (reply anchoring), and on main it also set the
presence bit in the prompt-pin key, so the note-gating cache miss this PR
fixes only surfaced for native slash-command joins. Clear it on the way out.

Review-fix for NousResearch#127944.

[adapted onto main: NousResearch#133463 resolves the speaker name inline in _voice_input_source, so the
clear lands in main's block; the test moved to test_discord_turn_prompt_parity.py, a typed
`/voice join` id on the bound source]
(cherry picked from commit fe70c11)
guild.get_member() is cache-only, so a speaker who had never typed in the
guild (no member intent, or joined voice straight away) rendered as the bare
user id while their text turns render a display name: the session-context
prompt differed again on every switch. Fall back to the cached global user
(global name / username) before giving up; a guild nick still wins when the
member is cached. Suggested in the first review, reproduced independently
(F3 on the thread).

[adapted onto main: NousResearch#133463 resolves the speaker name inline in _voice_input_source, not
through DiscordAdapter.voice_member_display_name, so the fallback is a small helper there. It
runs after the /voice join invoker's bound name, which is the nickname their typed turns
carry; a cached global name would differ from it. Tests moved to
test_discord_turn_prompt_parity.py]
(cherry picked from commit 295a01f)
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/gateway Gateway runner, session dispatch, delivery platform/discord Discord bot adapter sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages labels Oct 6, 2026
@unsupportedpastels
unsupportedpastels merged commit 4787e4d into NousResearch:main Oct 6, 2026
53 checks passed
@unsupportedpastels
unsupportedpastels deleted the fix/voice-turn-trigger-and-uncached-name branch October 6, 2026 02:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/gateway Gateway runner, session dispatch, delivery P2 Medium — degraded but workaround exists platform/discord Discord bot adapter sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants