Skip to content

fix(gateway): stop lease-wait refreshes flooding non-editing chat adapters - #89180

Open
liuhao1024 wants to merge 1 commit into
NousResearch:mainfrom
liuhao1024:liuhao/cron-bugfix-89166
Open

fix(gateway): stop lease-wait refreshes flooding non-editing chat adapters#89180
liuhao1024 wants to merge 1 commit into
NousResearch:mainfrom
liuhao1024:liuhao/cron-bugfix-89166

Conversation

@liuhao1024

Copy link
Copy Markdown
Contributor

What does this PR do?

While a durable session's cross-process turn lease is held by another Hermes process, the waiting process emits an initial "Another Hermes process is using this session…" notice once, then a periodic "Still waiting… (Ns)" refresh roughly every 15s. That refresh only makes sense on surfaces that can update the initial notice in place. On adapters without send_or_update_status (WeCom, Weixin, QQ, Signal, … — all SUPPORTS_MESSAGE_EDITING = False) the status path falls back to a plain adapter.send, so every refresh landed as another standalone chat message: a two-minute wait produced eight separate messages that drowned the eventual delivery.

This PR suppresses the periodic refresh when the adapter cannot update the status in place:

  • Extracts the refresh wording into a module-level template constant at the emit site.
  • Derives a compile-once matcher from that constant (same constants-are-the-wording convention as _COMPRESSION_PROGRESS_STATUS_RE, fix(gateway): suppress routine pre-API compression chatter on chat platforms #69550) and gates it on adapter capability in the status callback.
  • Adapters that CAN update in place (Telegram, Slack) keep refreshing the existing bubble unchanged.

The initial wait notice and the lease-timeout warning use different wording and are always delivered, on every platform.

Related Issue

Fixes #89166

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature
  • Breaking change
  • Documentation update

Changes Made

  • SESSION_TURN_LEASE_WAIT_REFRESH_STATUS_TEMPLATE: the periodic refresh wording as a format template; the emit site now formats it instead of an inline f-string.
  • _session_turn_lease_refresh_re(): lazy compile-once regex derived from that template via the existing _status_template_to_regex helper (lazy because this module never imports run_agent at module scope).
  • _should_suppress_lease_wait_refresh(adapter, message): True only when the adapter has no callable send_or_update_status AND the message is the periodic refresh.
  • _status_callback_sync: early-returns (with a debug log) when that helper says the refresh would flood this adapter.
  • New regression test suite covering: refresh suppressed across elapsed values on a no-update adapter, refresh delivered on an update-capable adapter, initial notice / timeout warning / unrelated lifecycle statuses never suppressed, and the refresh surviving the noise filter before the gate runs.

Related but distinct from #12610 (which suppresses ALL lifecycle events on the email platform by platform enum): this change is scoped to the single periodic lease-wait refresh template and keys off adapter capability, so every other lifecycle status keeps flowing to non-editing platforms.

How to Test

python -m pytest tests/gateway/test_session_lease_wait_refresh.py -q
# 5 passed

python -m pytest tests/gateway/test_telegram_noise_filter.py tests/gateway/test_compression_progress_notices.py tests/gateway/test_telegram_status_update.py tests/state/test_session_turn_lease.py tests/run_agent/test_cross_process_turn_lease.py -q
# all passed locally

Manual repro from the issue: open the same durable session from Desktop and send a DM through a WeCom/Weixin gateway while Desktop holds the turn; previously each 15s tick posted a new chat message, now only the single initial wait notice appears (and the lease-timeout warning if the wait exceeds the budget).

Fail-on-main check: with the source changes stashed, the new suite fails at collection (the gate and template constant do not exist on main).

Checklist

  • Code follows the project's style guidelines
  • Self-review completed
  • Comments explain intent (why the refresh must be capability-gated)
  • Corresponding changes documented via docstrings
  • Tests added and passing (5 new; adjacent suites green)
  • Single root cause, no unrelated changes

…pters

Cross-process session turn lease waits emit a periodic 'Still waiting'
refresh roughly every 15s. On adapters without send_or_update_status
the status path falls back to a plain send, so each refresh landed as
another standalone chat message — eight messages for a two-minute wait
on WeCom/Weixin/QQ/Signal-class adapters, drowning the eventual
delivery.

Extract the refresh wording into a module-level template constant and
derive a compile-once matcher from it, then suppress the periodic
refresh in the status callback when the adapter cannot update the
status in place. Adapters that can (Telegram, Slack) keep refreshing
the existing bubble. The initial wait notice and the lease-timeout
warning use different wording and are always delivered.
@alt-glitch alt-glitch added type/bug Something isn't working comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/gateway Gateway runner, session dispatch, delivery P2 Medium — degraded but workaround exists sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state labels Aug 18, 2026
@Enough1122

Copy link
Copy Markdown
Contributor

AI code review — automated review for reference; please use your judgment.

Reviewed by reviewer-e (AI automated review).

Clean fix for notification flooding with the right single-source discipline: the refresh wording moves into SESSION_TURN_LEASE_WAIT_REFRESH_STATUS_TEMPLATE and the gateway derives its matcher from that same constant (never re-inlining the prose — explicitly called out as the #69550 convention), so emitter and suppressor can't drift. Suppression keys on real adapter capability (send_or_update_status present) rather than a platform name list, the initial notice and lease-timeout warning are provably immune by wording difference, and the five tests cover suppressed/delivered/initial/timeout/unrelated shapes including a "refresh survives the noise filter first" assertion.

andrexibiza added a commit to andrexibiza/hermes-agent that referenced this pull request Sep 2, 2026
Rebuild NousResearch#100386 as an append-only composition instead of replacing contributor
history with a single-author squash.

The second-parent train preserves:
- David Dudok de Wit's NousResearch#97846/NousResearch#98073 continuity and messaging commits through
  exact source head 6fb588d.
- jugol's source-authored NousResearch#96162 forward port at
  22b26d0.
- liuhao1024's NousResearch#89180 lease-refresh fix through the adapted source-authored
  replay 6a74634.

The lease-status extraction at e3ec744
keeps gateway/run.py from growing, keeps run_agent.py shrinking, and preserves
the original import seams and behavior.

This merge commit changes no bytes relative to its second parent. Its first
parent is the published NousResearch#100386 head, so the correction is forward-only: no
force-push, rebase, amend, or source-history rewrite.

Related: NousResearch#97681
Fixes: NousResearch#89166
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/gateway Gateway runner, session dispatch, delivery P2 Medium — degraded but workaround exists sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Cross-process session lease wait floods chat gateways every 15s and starves final delivery

3 participants