mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-01 12:02:05 +00:00
test(gateway): pin auto-reset cached-agent eviction (#10710)
Relocate marco0158's eviction into the dedicated auto-reset cleanup block (single source of truth for dropping session-scoped transient state) and add an AST invariant pinning _evict_cached_agent into that block. Add AUTHOR_MAP entry for marco0158.
This commit is contained in:
parent
b4300f2d96
commit
0b733a8418
3 changed files with 100 additions and 5 deletions
|
|
@ -9682,6 +9682,13 @@ class GatewayRunner(GatewayAuthorizationMixin, GatewayKanbanWatchersMixin, Gatew
|
|||
self._set_session_reasoning_override(session_key, None)
|
||||
if hasattr(self, "_pending_model_notes"):
|
||||
self._pending_model_notes.pop(session_key, None)
|
||||
# Evict the cached agent so the fresh session does not inherit the
|
||||
# previous conversation's context_compressor._previous_summary —
|
||||
# the cache is keyed on the stable session_key, so an auto-reset
|
||||
# otherwise reuses the old agent and leaks prior history into new
|
||||
# compaction summaries. Mirrors /reset and the compression-exhausted
|
||||
# path (#9893). Covers daily/idle/suspended auto-reset.
|
||||
self._evict_cached_agent(session_key)
|
||||
session_entry.was_auto_reset = False
|
||||
|
||||
# Emit session:start for new or auto-reset sessions
|
||||
|
|
@ -9786,11 +9793,6 @@ class GatewayRunner(GatewayAuthorizationMixin, GatewayKanbanWatchersMixin, Gatew
|
|||
# (single source of truth); only the reset reason needs clearing here.
|
||||
session_entry.auto_reset_reason = None
|
||||
|
||||
# Evict cached agent to prevent stale context_compressor._previous_summary
|
||||
# from leaking into the new session after auto-reset (daily/idle/suspended).
|
||||
# Follow-up to #9893 which only handled compression_exhausted case.
|
||||
self._evict_cached_agent(session_key)
|
||||
|
||||
# Auto-load skill(s) for topic/channel bindings (Telegram DM Topics,
|
||||
# Discord channel_skill_bindings). Supports a single name or ordered list.
|
||||
# Only inject on NEW sessions — ongoing conversations already have the
|
||||
|
|
|
|||
|
|
@ -173,6 +173,7 @@ AUTHOR_MAP = {
|
|||
"290859878+synapsesx@users.noreply.github.com": "synapsesx",
|
||||
"157689911+itsflownium@users.noreply.github.com": "itsflownium",
|
||||
"dirtyren@users.noreply.github.com": "dirtyren",
|
||||
"mailtowbd@gmail.com": "marco0158",
|
||||
"157793278+jacobmansonlkevincc@users.noreply.github.com": "lkevincc0",
|
||||
"121278003+Cossackx@users.noreply.github.com": "Cossackx", # PR #52528 salvage (Windows hermes-shim resolution + prefer --update on recovery; #52378)
|
||||
"97326386+Icather@users.noreply.github.com": "Icather", # PR #45554 salvage (self-lock guard breaks Windows update-recovery infinite loop; #52378 / #45542)
|
||||
|
|
|
|||
92
tests/gateway/test_10710_auto_reset_evicts_cached_agent.py
Normal file
92
tests/gateway/test_10710_auto_reset_evicts_cached_agent.py
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
"""Regression test for #10710 — stale context summary leak after auto-reset.
|
||||
|
||||
The gateway agent cache is keyed on the stable chat ``session_key``, which does
|
||||
NOT change when a session is auto-reset (daily schedule / idle timeout /
|
||||
suspended). So unless the cached agent is explicitly evicted on auto-reset, the
|
||||
NEXT message reuses the old ``AIAgent`` instance — carrying its
|
||||
``context_compressor._previous_summary`` — and prior-conversation content leaks
|
||||
into the new session's compaction summaries.
|
||||
|
||||
Manual ``/reset`` and the compression-exhausted path (#9893) already evict the
|
||||
cached agent. This pins the matching eviction onto the auto-reset cleanup block
|
||||
in ``_handle_message_with_agent``.
|
||||
|
||||
These are AST invariants — load-bearing pins that fail if the eviction is
|
||||
removed from the cleanup block (mirrors
|
||||
test_48031_model_switch_after_auto_reset.py's approach).
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import ast
|
||||
import inspect
|
||||
|
||||
from gateway import run as gateway_run
|
||||
|
||||
|
||||
def _calls(node: ast.AST) -> set[str]:
|
||||
"""Method-call attribute names invoked anywhere under ``node``."""
|
||||
return {
|
||||
n.func.attr
|
||||
for n in ast.walk(node)
|
||||
if isinstance(n, ast.Call) and isinstance(n.func, ast.Attribute)
|
||||
}
|
||||
|
||||
|
||||
def _assigns_false(node: ast.AST, attr: str) -> bool:
|
||||
"""True if ``node`` contains an assignment ``<something>.<attr> = False``."""
|
||||
for sub in ast.walk(node):
|
||||
if isinstance(sub, ast.Assign):
|
||||
for tgt in sub.targets:
|
||||
if (
|
||||
isinstance(tgt, ast.Attribute)
|
||||
and tgt.attr == attr
|
||||
and isinstance(sub.value, ast.Constant)
|
||||
and sub.value.value is False
|
||||
):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def test_auto_reset_cleanup_evicts_cached_agent():
|
||||
"""The auto-reset cleanup block in gateway/run.py must call
|
||||
``_evict_cached_agent`` so the fresh session does not reuse the previous
|
||||
conversation's cached agent (and its leaked
|
||||
``context_compressor._previous_summary``) — the cache is keyed on the
|
||||
stable ``session_key`` (#10710)."""
|
||||
tree = ast.parse(inspect.getsource(gateway_run))
|
||||
|
||||
# Fingerprint the cleanup branch: the `if <was_auto_reset>:` block that
|
||||
# drops transient session state (calls the reasoning-override setter AND
|
||||
# consumes the flag by setting was_auto_reset = False). The eviction must
|
||||
# live in that same block.
|
||||
found = False
|
||||
for node in ast.walk(tree):
|
||||
if not isinstance(node, ast.If):
|
||||
continue
|
||||
calls = _calls(node)
|
||||
if (
|
||||
"_set_session_reasoning_override" in calls
|
||||
and _assigns_false(node, "was_auto_reset")
|
||||
):
|
||||
assert "_evict_cached_agent" in calls, (
|
||||
"gateway/run.py auto-reset cleanup block must call "
|
||||
"`_evict_cached_agent(session_key)` so the auto-reset session "
|
||||
"does not reuse the previous cached agent and leak its "
|
||||
"context_compressor._previous_summary into new compaction "
|
||||
"summaries (#10710)."
|
||||
)
|
||||
found = True
|
||||
break
|
||||
assert found, (
|
||||
"could not locate the auto-reset transient-state cleanup block in "
|
||||
"gateway/run.py (fingerprint: _set_session_reasoning_override + "
|
||||
"was_auto_reset = False)."
|
||||
)
|
||||
|
||||
|
||||
def test_evict_cached_agent_method_exists():
|
||||
"""The eviction helper the cleanup relies on must exist on the runner."""
|
||||
assert hasattr(gateway_run.GatewayRunner, "_evict_cached_agent"), (
|
||||
"GatewayRunner._evict_cached_agent is the helper the auto-reset "
|
||||
"cleanup depends on (#10710)."
|
||||
)
|
||||
Loading…
Add table
Add a link
Reference in a new issue