From 165c25a831f72ee278b21f808b8ac049155e4b7e Mon Sep 17 00:00:00 2001 From: annguyenNous Date: Tue, 16 Jun 2026 08:35:48 +0700 Subject: [PATCH] fix(gateway): add freshness gate to resume_pending in get_or_create_session MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a session has resume_pending=True but the freshness window has expired (default 1 hour), the session is a zombie — the auto-recovery turn either never ran or failed. Previously, get_or_create_session() returned the stale session unconditionally, causing context bleed where the agent would respond with old conversation context. Fix: check last_resume_marked_at against HERMES_AUTO_CONTINUE_FRESHNESS window. If expired, fall through to auto-reset with reason "resume_pending_expired" instead of returning the stale entry. Fixes NousResearch/hermes-agent#46934 --- gateway/session.py | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/gateway/session.py b/gateway/session.py index 5548139a682ed..bbbb668384c21 100644 --- a/gateway/session.py +++ b/gateway/session.py @@ -926,9 +926,24 @@ def get_or_create_session( # the NEXT successful turn completes (not here), which # means a re-interrupted retry keeps trying — the # stuck-loop counter handles terminal escalation. - entry.updated_at = now - self._save() - return entry + # + # Freshness gate: if the session has been pending + # resume longer than the auto-continue freshness + # window, treat it as a zombie — the recovery turn + # either never ran or failed and the stale transcript + # would pollute the context (#46934). + _ref_time = entry.last_resume_marked_at or entry.updated_at + _fw_raw = os.environ.get("HERMES_AUTO_CONTINUE_FRESHNESS") + try: + _fw = float(_fw_raw) if _fw_raw else 3600.0 + except (TypeError, ValueError): + _fw = 3600.0 + if _fw > 0 and (now - _ref_time).total_seconds() > _fw: + reset_reason = "resume_pending_expired" + else: + entry.updated_at = now + self._save() + return entry else: reset_reason = self._should_reset(entry, source) if not reset_reason: