Skip to content

fix(gateway): stop typing before final delivery - #52490

Open
ychoi-kr wants to merge 1 commit into
NousResearch:mainfrom
ychoi-kr:fix/discord-typing-stop-before-final-delivery
Open

fix(gateway): stop typing before final delivery#52490
ychoi-kr wants to merge 1 commit into
NousResearch:mainfrom
ychoi-kr:fix/discord-typing-stop-before-final-delivery

Conversation

@ychoi-kr

Copy link
Copy Markdown

What does this PR do?

Stops the gateway typing refresh before final response delivery starts.

GatewayRunner._run_agent() already calls adapter.stop_typing() after the agent returns, but BasePlatformAdapter._keep_typing() can still be alive until _process_message_background() reaches its later cleanup path. On Discord, a late _keep_typing() tick can re-enter send_typing() after that handler-level stop and just before _send_with_retry() delivers the final answer. Because Discord has no explicit "typing stop" endpoint, that last typing pulse can leave the client showing "bot is typing" after the final response has already appeared.

This patch stops the base typing refresh immediately after the message handler returns and before the final send/media delivery path begins. The existing finally-block cleanup is kept as a fallback.

Related issues / PRs

Changes made

  • gateway/platforms/base.py
    • Calls _stop_typing_task() immediately after _message_handler(event) returns and before final response delivery.
    • Documents why this boundary matters for Discord's TTL-based typing indicator.
  • tests/gateway/test_typing_stop_before_delivery.py
    • Adds a regression test asserting that final response send does not begin until stop_typing() has fired.

Verification

Run on macOS from this checkout:

venv/bin/python -m pytest tests/gateway/test_typing_stop_before_delivery.py -q -o 'addopts='
# 1 passed in 0.48s

venv/bin/python -m pytest \
  tests/gateway/test_pending_drain_race.py \
  tests/gateway/test_pending_drain_no_recursion.py \
  tests/gateway/test_typing_stop_before_delivery.py \
  -q -o 'addopts='
# 9 passed in 5.52s

venv/bin/python -m pytest \
  tests/gateway/test_discord_send.py \
  tests/gateway/test_discord_imports.py \
  tests/gateway/test_typing_stop_before_delivery.py \
  -q -o 'addopts='
# 20 passed in 1.38s

git diff --check
python -m py_compile gateway/platforms/base.py tests/gateway/test_typing_stop_before_delivery.py
# clean

Manual verification: reproduced in a Discord DM on current main after a long turn (time=199.0s api_calls=16), applied this patch, restarted the gateway, and confirmed the "Atropos is typing" indicator no longer lingers after the final response.

Notes

This is intentionally a small lifecycle-ordering fix. It does not remove or redesign Discord's adapter-level typing behavior; PR #43691 may still be a better long-term fix for orphaned Discord typing loops. This patch closes the remaining race where the base refresh loop can issue a final typing pulse after the agent has returned but before final response delivery begins.

@alt-glitch alt-glitch added type/bug Something isn't working comp/gateway Gateway runner, session dispatch, delivery platform/discord Discord bot adapter P3 Low — cosmetic, nice to have sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages labels Jun 25, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

Competing fix for the typing-indicator race. The most comprehensive open PR in this cluster is #29172 (covers the stream consumer + all delivery paths via per-chat stop events); this PR instead stops the base typing refresh at the _message_handler return boundary. Other approaches in the cluster: #25210, #43390 (both closed). Flagging for a maintainer to pick the canonical mechanism — not marking either as a duplicate.

@teknium1

Copy link
Copy Markdown
Contributor

Thanks for isolating the pre-delivery lifecycle boundary. The premise remains valid on the inspected current main: gateway/platforms/base.py:4882 returns from the handler and final text can begin at lines 5029-5034, while refresh shutdown is currently deferred to cleanup at line 5259.

Problems

  • The surrounding lifecycle has been refactored since this PR’s diff. Current main’s local _stop_typing_task() delegates to the atomic _stop_typing_refresh() helper (gateway/platforms/base.py:4872-4876; helper at lines 3887-3917), so the insertion needs a small transplant at the current handler-return boundary rather than a blind application of the old hunk.

Suggested changes

  • Preserve the proposed ordering immediately after response = await self._message_handler(event) at gateway/platforms/base.py:4882, before response processing and all final delivery paths.

This is an automated hermes-sweeper review.

@teknium1 teknium1 added the sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform label Jul 15, 2026
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 P3 Low — cosmetic, nice to have platform/discord Discord bot adapter sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform 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.

Discord: typing indicator lingers after response is sent

3 participants