fix(gateway): notify user and write correct end_reason for resume_pending_expired resets - #58935
Open
hejuntt1014 wants to merge 1 commit into
Open
hejuntt1014 wants to merge 1 commit into
hejuntt1014 wants to merge 1 commit into
Conversation
…ng_expired resets
When a gateway session with resume_pending=True is not recovered within the
auto-continue freshness window (e.g. because repeated API calls timed out on a
large context), get_or_create_session correctly creates a new session. However
two gaps existed:
1. The user received no notification — resume_pending_expired fell through the
generic "inactive for Xh" else-branch in run.py, which produces wrong wording
and (for session_reset.mode: none users) is gated on policy.notify that
evaluates to False.
2. The old session was ended in state.db with the hardcoded generic reason
"session_reset", making it impossible to distinguish from a normal idle/daily
reset in post-mortem analysis.
Fix:
- gateway/run.py: add an explicit resume_pending_expired case for the agent
context note ("gateway restart recovery timed out") and the user-facing
notification. Always notify for this reason — like suspended — because the
user had an active session that was silently replaced.
- gateway/session.py: pass auto_reset_reason as the DB end_reason instead of
the hardcoded "session_reset", so all auto-reset paths are auditable.
- tests: extend TestResumePendingExpiredAutoReset in test_session_reset_notify.py
with five new cases that cover the reason, activity flag, DB end_reason,
non-regression of the idle path, and freshness-disabled bypass.
Closes NousResearch#58933
Co-authored-by: Cursor <cursoragent@cursor.com>
14 tasks
Collaborator
|
Thanks for the focused gateway fix. The premise is confirmed on current main: Problems
Suggested changes
Automated hermes-sweeper review. |
This branch has not been deployed
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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?
When a gateway session has
resume_pending=True(set after a drain-timeout restart) and API recovery fails — e.g. repeated timeouts on a large context — theresume_pendingmarker is never cleared. Aftergateway_auto_continue_freshnessseconds the next inbound message silently creates a new session. Two implementation gaps existed in that path:resume_pending_expiredfell into the genericelsebranch in_handle_message_with_agent, which produced the wrong wording ("inactive for Xh") and, forsession_reset.mode: noneusers, was gated behindpolicy.notify— which evaluates toFalseon that mode — so no notice was ever sent."session_reset", making it impossible to distinguish a restart-recovery timeout from a normal idle/daily reset in post-mortem analysis.This PR fixes both gaps:
resume_pending_expiredcase with correct agent system note and user notification (always fires, likesuspended)auto_reset_reasonas the DBend_reasonso every auto-reset path is auditableRelated Issue
Fixes #58933
Type of Change
Changes Made
gateway/run.py(_handle_message_with_agent,_was_auto_resetblock):elif reset_reason == "resume_pending_expired":for the agent context note:"gateway restart recovery timed out""resume_pending_expired"to theshould_notifyalways-on set (alongside"suspended") — restores history-context awareness without depending onsession_resetpolicy configelif reset_reason == "resume_pending_expired":forreason_textin the user-facing notice:"gateway restart recovery timed out"gateway/session.py(get_or_create_session, SQLite section outside lock):"session_reset"withauto_reset_reason if auto_reset_reason else "session_reset", soresume_pending_expired,suspended,idle, anddailyresets all record their specific reason instate.dbtests/gateway/test_session_reset_notify.py:_make_db_mock()helper with safe defaults for all DB lookup methods (prevents MagicMock leaking intosession_idvia the compression-tip heal path)TestResumePendingExpiredAutoResetclass with 5 new tests:test_stale_resume_pending_sets_auto_reset_reason—auto_reset_reason == "resume_pending_expired"when freshness expirestest_stale_resume_pending_had_activity_flag—reset_had_activityreflects token usagetest_stale_resume_pending_db_end_reason_is_specific—db.end_sessioncalled with"resume_pending_expired"not"session_reset"test_idle_reset_db_end_reason_reflects_idle— idle path non-regressiontest_freshness_disabled_skips_resume_pending_expired—HERMES_AUTO_CONTINUE_FRESHNESS=0disables the gateHow to Test
Skipping .clean_shutdown marker)resume_pending=Trueinsessions.jsonresume_pendingis never clearedgateway_auto_continue_freshness(default 3600s) then send a message"session_reset"in state.db◐ Session automatically reset (gateway restart recovery timed out). ..., old session ends with"resume_pending_expired"in state.dbOr run the new unit tests:
Checklist
Code
fix(scope):,feat(scope):, etc.)pytest tests/gateway/test_session_reset_notify.py -vand all 21 tests passTestResumePendingExpiredAutoReset)Documentation & Housekeeping
cli-config.yaml.exampleif I added/changed config keys — or N/A (no new config keys)Screenshots / Logs
Log evidence from two independent production profiles (dingding, bala) that triggered this bug simultaneously at ~19:22 on 2026-07-05 (~1h52m after the 17:30 restart):
After this fix, both users would have received:
Made with Cursor