Skip to content

fix(discord): use gateway event loop for approval box coroutines (#74470) - #74493

Closed
webtecnica wants to merge 4 commits into
NousResearch:mainfrom
webtecnica:fix/74470-discord-approval-loop
Closed

fix(discord): use gateway event loop for approval box coroutines (#74470)#74493
webtecnica wants to merge 4 commits into
NousResearch:mainfrom
webtecnica:fix/74470-discord-approval-loop

Conversation

@webtecnica

Copy link
Copy Markdown
Contributor

Summary

Fixes #74470 — Discord approval boxes were using the wrong asyncio event loop, causing Timeout context manager should be used inside a task from aiohttp.

Root Cause

The discord.py client's aiohttp session is bound to the gateway event loop. Sync tool-handler contexts were scheduling adapter coroutines through model_tools._run_async(), which creates a private/per-worker event loop. When channel.send() tried to use the aiohttp session from the wrong loop, it raised "Timeout context manager should be used inside a task".

Changes

gateway/platforms/base.py

  • Added _gateway_loop class attribute to BasePlatformAdapter (analogous to gateway_runner) so every adapter can know its owning event loop.

gateway/run.py

  • Gateway runner now injects _gateway_loop alongside gateway_runner at all three adapter-creation sites (plugin-registered platforms, APIServerAdapter, WebhookAdapter).

plugins/platforms/discord/adapter.py

  • Added self._gateway_loop to DiscordAdapter.__init__().
  • Added _run_coro_on_loop() helper that schedules coroutines on the stored gateway loop via asyncio.run_coroutine_threadsafe (thread-safe, preserves aiohttp session binding). Falls back to _run_async() when no gateway loop is available (standalone/cron).

tests/gateway/test_discord_exec_approval_content.py

  • Added test_run_coro_on_loop_uses_gateway_loop — proves coroutines run on the correct loop and _run_async is NOT called when the gateway loop is available.
  • Added test_run_coro_on_loop_falls_back_to_run_async_without_gateway_loop — proves the helper falls back gracefully for standalone contexts.
  • Existing render tests pass unchanged.

Testing

  • 228 related tests pass (Discord clarify, approval mentions, prompt timeout, config).
  • 4/4 new + existing test-exec-approval tests pass.
  • Regression tests prove the owning loop is used and _run_async is bypassed when the gateway loop is available.

webtecnica and others added 4 commits July 27, 2026 13:14
When a desktop zone (e.g. terminal panel) is minimized/collapsed, the
header collapse button now shows chevron-up instead of chevron-down.
Chevron-down indicates 'click to collapse' (expanded state), while
chevron-up indicates 'click to restore' (collapsed state).

Fixes NousResearch#74006
…sResearch#74470)

The discord.py client's aiohttp session is bound to the gateway event loop.
Previously, sync tool-handler contexts scheduled adapter coroutines through
model_tools._run_async(), which creates a private/per-worker event loop
— causing aiohttp to reject with "Timeout context manager should be used
inside a task".

Fix:
- Add _gateway_loop attribute to BasePlatformAdapter, set by the
  gateway runner alongside gateway_runner so every adapter knows its
  owning loop.
- Add DiscordAdapter._run_coro_on_loop() helper that schedules
  coroutines on the stored gateway loop via
  asyncio.run_coroutine_threadsafe (thread-safe, preserves session
  binding), falling back to _run_async() for standalone/cron contexts.
- Gateway runner injects _gateway_loop at all three adapter-creation
  sites.

Regression tests prove:
1. With _gateway_loop set, the coroutine runs on the correct loop.
2. _run_async is never called when the gateway loop is available.
3. Without _gateway_loop, the helper falls back to _run_async.
4. Existing three-control render tests pass unchanged.
@alt-glitch alt-glitch added type/bug Something isn't working P3 Low — cosmetic, nice to have comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/desktop Electron desktop app (apps/desktop/*) comp/gateway Gateway runner, session dispatch, delivery comp/plugins Plugin system and bundled plugins platform/discord Discord bot adapter sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages duplicate This issue or pull request already exists labels Jul 30, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

Duplicate of cleaner focused #74471 for the Discord approval-box gateway-loop repair. This branch also carries unrelated agent, Desktop, and Himalaya changes that should not be merged with that fix.

@teknium1

Copy link
Copy Markdown
Contributor

Thanks for the report and the focused regression investigation. This automated hermes-sweeper review found that the requested gateway-loop behavior is already implemented on current main.

  • gateway/run.py:4832 schedules send_exec_approval(...) with safe_schedule_threadsafe(..., ctx._loop_for_step) rather than model_tools._run_async.
  • gateway/run.py:23565 binds ctx._loop_for_step from asyncio.get_running_loop(), so the send executes on the gateway's active event loop.
  • The same scheduling path was already present in this PR's base commit d26983e4856e; current main retains it through 1a3a9de630a.
  • @alt-glitch also noted the duplicate focused work in fix(discord): schedule approval boxes on the gateway loop #74471 and that this branch contains unrelated commits.

Closing as implemented on main.

@teknium1 teknium1 closed this Jul 30, 2026
@teknium1 teknium1 added the sweeper:implemented-on-main Sweeper: behavior already present on current main label Jul 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/desktop Electron desktop app (apps/desktop/*) comp/gateway Gateway runner, session dispatch, delivery comp/plugins Plugin system and bundled plugins duplicate This issue or pull request already exists P3 Low — cosmetic, nice to have platform/discord Discord bot adapter sweeper:implemented-on-main Sweeper: behavior already present on current main 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 approval boxes use the wrong asyncio event loop

3 participants