{"repo":"NousResearch/hermes-agent","public_id":"3784","display_label":"Suppress or remove compression/context pressure warnings","summary":"These issues and PRs concern adding a configuration option to suppress compression or context pressure warning messages, or removing such warnings entirely from user-facing output.","subsystem":"agent","counts":{"members":4,"open_issues":0,"open_issues_distinct":0,"open_prs":0,"closed_issues":1,"merged_prs":2,"attempt_prs":1},"landed_fix":{"status":"merged_to_main","basis":"structural","pr":11039,"merged_at":"2026-04-16T13:44:24+00:00"},"resolution":{"state":"resolved","by":"pr-11039"},"freshness":{"state":"stale","last_verified_at":"2026-04-16T13:44:24+00:00","is_stale":true},"grouping":{"backbone":"STRUCTURAL_SPANNING","link_density":0.5},"fix_edges":[{"issue_number":3784,"pr_number":4139,"tier":"verified","basis":"superseded_ref","confidence":0.9,"recoverable_from_timeline":false,"pr_merged_at":"2026-03-31T02:17:08+00:00"},{"issue_number":3784,"pr_number":11039,"tier":"verified","basis":"closing_ref","confidence":0.95,"recoverable_from_timeline":true,"pr_merged_at":"2026-04-16T13:44:24+00:00"}],"members":[{"kind":"issue","number":3784,"title":"Add option to turn off the compression warning message","url":null,"state":"closed"},{"kind":"pr","number":4139,"title":"fix(gateway): remove user-facing compression warnings","url":null,"state":"merged","close_reason":{"category":"merged"},"merged":true,"is_fix_pr":true,"fix_tier":"verified","files_changed":["gateway/run.py","tests/gateway/test_session_hygiene.py"]},{"kind":"pr","number":10840,"title":"feat: add compression.warnings config option to suppress context pressure alerts","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#4139","number":4139},{"kind":"pr","value":"#11039","number":11039},{"kind":"pr","value":"#3784","number":3784}],"category":"merged_via","implication":"superseded"},"merged":false},{"kind":"pr","number":11039,"title":"fix: remove context pressure warnings entirely","url":null,"state":"merged","close_reason":{"category":"merged"},"merged":true,"is_fix_pr":true,"fix_tier":"verified","files_changed":["agent/display.py","run_agent.py","tests/run_agent/test_context_pressure.py"],"fix_hunks":[{"file":"agent/display.py","hunks":["@@ -993,84 +993,4 @@ def _wrap(line: str) -> str:\n # Honcho session line (one-liner with clickable OSC 8 hyperlink)\n # =========================================================================\n \n-_DIM = \"\\033[2m\"\n-_SKY_BLUE = \"\\033[38;5;117m\"\n-_ANSI_RESET = \"\\033[0m\"\n-\n-\n-# =========================================================================\n-# Context pressure display (CLI user-facing warnings)\n-# =========================================================================\n-\n-# ANSI color codes for context pressure tiers\n-_CYAN = \"\\033[36m\"\n-_YELLOW = \"\\033[33m\"\n-_BOLD = \"\\033[1m\"\n-_DIM_ANSI = \"\\033[2m\"\n-\n-# Bar characters\n-_BAR_FILLED = \"▰\"\n-_BAR_EMPTY = \"▱\"\n-_BAR_WIDTH = 20\n-\n-\n-def format_context_pressure(\n-    compaction_progress: float,\n-    threshold_tokens: int,\n-    threshold_percent: float,\n-    compression_enabled: bool = True,\n-) -> str:\n-    \"\"\"Build a formatted context pressure line for CLI display.\n-\n-    The bar and percentage show progress toward the compaction threshold,\n-    NOT the raw context window.  100% = compaction fires.\n-\n-    Args:\n-        compaction_progress: How close to compaction (0.0–1.0, 1.0 = fires).\n-        threshold_tokens: Compaction threshold in tokens.\n-        threshold_percent: Compaction threshold as a fraction of context window.\n-        compression_enabled: Whether auto-compression is active.\n-    \"\"\"\n-    pct_int = min(int(compaction_progress * 100), 100)\n-    filled = min(int(compaction_progress * _BAR_WIDTH), _BAR_WIDTH)\n-    bar = _BAR_FILLED * filled + _BAR_EMPTY * (_BAR_WIDTH - filled)\n-\n-    threshold_k = f\"{threshold_tokens // 1000}k\" if threshold_tokens >= 1000 else str(threshold_tokens)\n-    threshold_pct_int = int(threshold_percent * 100)\n-\n-    color = f\"{_BOLD}{_YELLOW}\"\n-    icon = \"⚠\"\n-    if compression_enabled:\n-        hint = \"compaction approaching\"\n-    else:\n-        hint = \"no auto-compaction\"\n-\n-    return (\n-        f\"  {color}{icon} context {bar} {pct_int}% to compaction{_ANSI_RESET}\"\n-        f\"  {_DIM_ANSI}{threshold_k} threshold ({threshold_pct_int}%) · {hint}{_ANSI_RESET}\"\n-    )\n-\n-\n-def format_context_pressure_gateway(\n-    compaction_progress: float,\n-    threshold_percent: float,\n-    compression_enabled: bool = True,\n-) -> str:\n-    \"\"\"Build a plain-text context pressure notification for messaging platforms.\n-\n-    No ANSI — just Unicode and plain text suitable for Telegram/Discord/etc.\n-    The percentage shows progress toward the compaction threshold.\n-    \"\"\"\n-    pct_int = min(int(compaction_progress * 100), 100)\n-    filled = min(int(compaction_progress * _BAR_WIDTH), _BAR_WIDTH)\n-    bar = _BAR_FILLED * filled + _BAR_EMPTY * (_BAR_WIDTH - filled)\n-\n-    threshold_pct_int = int(threshold_percent * 100)\n-\n-    icon = \"⚠️\"\n-    if compression_enabled:\n-        hint = f\"Context compaction approaching (threshold: {threshold_pct_int}% of window).\"\n-    else:\n-        hint = \"Auto-compaction is disabled — context may be truncated.\"\n \n-    return f\"{icon} Context: {bar} {pct_int}% to compaction\\n{hint}\""]},{"file":"run_agent.py","hunks":["@@ -540,13 +540,6 @@ class AIAgent:\n     for AI models that support function calling.\n     \"\"\"\n \n-    # ── Class-level context pressure dedup (survives across instances) ──\n-    # The gateway creates a new AIAgent per message, so instance-level flags\n-    # reset every time.  This dict tracks {session_id: (warn_level, timestamp)}\n-    # to suppress duplicate warnings within a cooldown window.\n-    _context_pressure_last_warned: dict = {}\n-    _CONTEXT_PRESSURE_COOLDOWN = 300  # seconds between re-warning same session\n-\n     @property\n     def base_url(self) -> str:\n         return self._base_url","@@ -826,12 +819,6 @@ def __init__(\n         self._budget_exhausted_injected = False\n         self._budget_grace_call = False\n \n-        # Context pressure warnings: notify the USER (not the LLM) as context\n-        # fills up.  Purely informational — displayed in CLI output and sent via\n-        # status_callback for gateway platforms.  Does NOT inject into messages.\n-        # Tiered: fires at 85% and again at 95% of compaction threshold.\n-        self._context_pressure_warned_at = 0.0  # highest tier already shown\n-\n         # Activity tracking — updated on each API call, tool execution, and\n         # stream chunk.  Used by the gateway timeout handler to report what the\n         # agent was doing when it was killed, and by the \"still working\"","@@ -7220,20 +7207,6 @@ def _compress_context(self, messages: list, system_message: str, *, approx_token\n         self.context_compressor.last_prompt_tokens = [redacted:secret-assignment]\n         self.context_compressor.last_completion_tokens = 0\n \n-        # Only reset the pressure warning if compression actually brought\n-        # us below the warning level (85% of threshold).  When compression\n-        # can't reduce enough (e.g. threshold is very low, or system prompt\n-        # alone exceeds the warning level), keep the tier set to prevent\n-        # spamming the user with repeated warnings every loop iteration.\n-        if self.context_compressor.threshold_tokens > 0:\n-            _post_progress = _compressed_est / self.context_compressor.threshold_tokens\n-            if _post_progress < 0.85:\n-                self._context_pressure_warned_at = 0.0\n-                # Clear class-level dedup for this session so a fresh\n-                # warning cycle can start if context grows again.\n-                _sid = self.session_id or \"default\"\n-                AIAgent._context_pressure_last_warned.pop(_sid, None)\n-\n         # Clear the file-read dedup cache.  After compression the original\n         # read content is summarised away — if the model re-reads the same\n         # file it needs the full content, not a \"file unchanged\" stub.","@@ -8033,45 +8006,6 @@ def _execute_tool_calls_sequential(self, assistant_message, messages: list, effe\n \n \n \n-    def _emit_context_pressure(self, compaction_progress: float, compressor) -> None:\n-        \"\"\"Notify the user that context is approaching the compaction threshold.\n-\n-        Args:\n-            compaction_progress: How close to compaction (0.0–1.0, where 1.0 = fires).\n-            compressor: The ContextCompressor instance (for threshold/context info).\n-\n-        Purely user-facing — does NOT modify the message stream.\n-        For CLI: prints a formatted line with a progress bar.\n-        For gateway: fires status_callback so the platform can send a chat message.\n-        \"\"\"\n-        from agent.display import format_context_pressure, format_context_pressure_gateway\n-\n-        threshold_pct = compressor.threshold_tokens / compressor.context_length if compressor.context_length else 0.5\n-\n-        # CLI output — always shown (these are user-facing status notifications,\n-        # not verbose debug output, so they bypass quiet_mode).\n-        # Gateway users also get the callback below.\n-        if self.platform in (None, \"cli\"):\n-            line = format_context_pressure(\n-                compaction_progress=compaction_progress,\n-                threshold_tokens=[redacted:secret-assignment]\n-                threshold_percent=threshold_pct,\n-                compression_enabled=self.compression_enabled,\n-            )\n-            self._safe_print(line)\n-\n-        # Gateway / external consumers\n-        if self.status_callback:\n-            try:\n-                msg = format_context_pressure_gateway(\n-                    compaction_progress=compaction_progress,\n-                    threshold_percent=threshold_pct,\n-                    compression_enabled=self.compression_enabled,\n-                )\n-                self.status_callback(\"context_pressure\", msg)\n-            except Exception:\n-                logger.debug(\"status_callback error in context pressure\", exc_info=True)\n-\n     def _handle_max_iterations(self, messages: list, api_call_count: int) -> str:\n         \"\"\"Request a summary when max iterations are reached. Returns the final response text.\"\"\"\n         print(f\"⚠️  Reached maximum iterations ({self.max_iterations}). Requesting summary...\")","@@ -10792,38 +10726,6 @@ def _stop_spinner():\n                     else:\n                         _real_tokens = [redacted:secret-assignment]\n \n-                    # ── Context pressure warnings (user-facing only) ──────────\n-                    # Notify the user (NOT the LLM) as context approaches the\n-                    # compaction threshold.  Thresholds are relative to where\n-                    # compaction fires, not the raw context window.\n-                    # Does not inject into messages — just prints to CLI output\n-                    # and fires status_callback for gateway platforms.\n-                    # Tiered: 85% (orange) and 95% (red/critical).\n-                    if _compressor.threshold_tokens > 0:\n-                        _compaction_progress = _real_tokens / _compressor.threshold_tokens\n-                        # Determine the warning tier for this progress level\n-                        _warn_tier = 0.0\n-                        if _compaction_progress >= 0.95:\n-                            _warn_tier = 0.95\n-                        elif _compaction_progress >= 0.85:\n-                            _warn_tier = 0.85\n-                        if _warn_tier > self._context_pressure_warned_at:\n-                            # Class-level dedup: check if this session was already\n-                            # warned at this tier within the cooldown window.\n-                            _sid = self.session_id or \"default\"\n-                            _last = AIAgent._context_pressure_last_warned.get(_sid)\n-                            _now = time.time()\n-                            if _last is None or _last[0] < _warn_tier or (_now - _last[1]) >= self._CONTEXT_PRESSURE_COOLDOWN:\n-                                self._context_pressure_warned_at = _warn_tier\n-                                AIAgent._context_pressure_last_warned[_sid] = (_warn_tier, _now)\n-                                self._emit_context_pressure(_compaction_progress, _compressor)\n-                                # Evict stale entries (older than 2x cooldown)\n-                                _cutoff = _now - self._CONTEXT_PRESSURE_COOLDOWN * 2\n-                                AIAgent._context_pressure_last_warned = {\n-                                    k: v for k, v in AIAgent._context_pressure_last_warned.items()\n-                                    if v[1] > _cutoff\n-                                }\n-\n                     if self.compression_enabled and _compressor.should_compress(_real_tokens):\n                         self._safe_print(\"  ⟳ compacting context…\")\n                         messages, active_system_prompt = self._compress_context("]},{"file":"/dev/null","hunks":["@@ -1,361 +0,0 @@\n-\"\"\"Tests for context pressure warnings (user-facing, not injected into messages).\n-\n-Covers:\n-- Display formatting (CLI and gateway variants)\n-- Flag tracking and threshold logic on AIAgent\n-- Flag reset after compression\n-- status_callback invocation\n-\"\"\"\n-\n-import json\n-from types import SimpleNamespace\n-from unittest.mock import MagicMock, patch\n-\n-import pytest\n-\n-from agent.display import format_context_pressure, format_context_pressure_gateway\n-from run_agent import AIAgent\n-\n-\n-# ---------------------------------------------------------------------------\n-# Display formatting tests\n-# ---------------------------------------------------------------------------\n-\n-\n-class TestFormatContextPressure:\n-    \"\"\"CLI context pressure display (agent/display.py).\n-\n-    The bar shows progress toward the compaction threshold, not the\n-    raw context window.  60% = 60% of the way to compaction.\n-    \"\"\"\n-\n-    def test_80_percent_uses_warning_icon(self):\n-        line = format_context_pressure(0.80, 100_000, 0.50)\n-        assert \"⚠\" in line\n-        assert \"80% to compaction\" in line\n-\n-    def test_90_percent_uses_warning_icon(self):\n-        line = format_context_pressure(0.90, 100_000, 0.50)\n-        assert \"⚠\" in line\n-        assert \"90% to compaction\" in line\n-\n-    def test_bar_length_scales_with_progress(self):\n-        line_80 = format_context_pressure(0.80, 100_000, 0.50)\n-        line_95 = format_context_pressure(0.95, 100_000, 0.50)\n-        assert line_95.count(\"▰\") > line_80.count(\"▰\")\n-\n-    def test_shows_threshold_tokens(self):\n-        line = format_context_pressure(0.80, 100_000, 0.50)\n-        assert \"100k\" in line\n-\n-    def test_small_threshold(self):\n-        line = format_context_pressure(0.80, 500, 0.50)\n-        assert \"500\" in line\n-\n-    def test_shows_threshold_percent(self):\n-        line = format_context_pressure(0.80, 100_000, 0.50)\n-        assert \"50%\" in line\n-\n-    def test_approaching_hint(self):\n-        line = format_context_pressure(0.80, 100_000, 0.50)\n-        assert \"compaction approaching\" in line\n-\n-    def test_no_compaction_when_disabled(self):\n-        line = format_context_pressure(0.85, 100_000, 0.50, compression_enabled=False)\n-        assert \"no auto-compaction\" in line\n-\n-    def test_returns_string(self):\n-        result = format_context_pressure(0.65, 128_000, 0.50)\n-        assert isinstance(result, str)\n-\n-    def test_over_100_percent_capped(self):\n-        \"\"\"Progress > 1.0 should cap both bar and percentage text at 100%.\"\"\"\n-        line = format_context_pressure(1.05, 100_000, 0.50)\n-        assert \"▰\" in line\n-        assert line.count(\"▰\") == 20\n-        assert \"100%\" in line\n-        assert \"105%\" not in line\n-\n-\n-class TestFormatContextPressureGateway:\n-    \"\"\"Gateway (plain text) context pressure display.\"\"\"\n-\n-    def test_80_percent_warning(self):\n-        msg = format_context_pressure_gateway(0.80, 0.50)\n-        assert \"80% to compaction\" in msg\n-        assert \"50%\" in msg\n-\n-    def test_90_percent_warning(self):\n-        msg = format_context_pressure_gateway(0.90, 0.50)\n-        assert \"90% to compaction\" in msg\n-        assert \"approaching\" in msg\n-\n-    def test_no_compaction_warning(self):\n-        msg = format_context_pressure_gateway(0.85, 0.50, compression_enabled=False)\n-        assert \"disabled\" in msg\n-\n-    def test_no_ansi_codes(self):\n-        msg = format_context_pressure_gateway(0.80, 0.50)\n-        assert \"\\033[\" not in msg\n-\n-    def test_has_progress_bar(self):\n-        msg = format_context_pressure_gateway(0.80, 0.50)\n-        assert \"▰\" in msg\n-\n-    def test_over_100_percent_capped(self):\n-        \"\"\"Progress > 1.0 should cap percentage text at 100%.\"\"\"\n-        msg = format_context_pressure_gateway(1.09, 0.50)\n-        assert \"100% to compaction\" in msg\n-        assert \"109%\" not in msg\n-        assert msg.count(\"▰\") == 20\n-\n-\n-# ---------------------------------------------------------------------------\n-# AIAgent context pressure flag tests\n-# ---------------------------------------------------------------------------\n-\n-\n-def _make_tool_defs(*names):\n-    return [\n-        {\n-            \"type\": \"function\",\n-            \"function\": {\n-                \"name\": n,\n-                \"description\": f\"{n} tool\",\n-                \"parameters\": {\"type\": \"object\", \"properties\": {}},\n-            },\n-        }\n-        for n in names\n-    ]\n-\n-\n[redacted:email]()\n-def agent():\n-    \"\"\"Minimal AIAgent with mocked internals.\"\"\"\n-    with (\n-        patch(\"run_agent.get_tool_definitions\", return_value=_make_tool_defs(\"web_search\")),\n-        patch(\"run_agent.check_toolset_requirements\", return_value={}),\n-        patch(\"run_agent.OpenAI\"),\n-    ):\n-        a = AIAgent(\n-            api_key=\"[redacted:secret-assignment]\",\n-            quiet_mode=True,\n-            skip_context_files=True,\n-            skip_memory=True,\n-        )\n-        a.client = MagicMock()\n-        return a\n-\n-\n-class TestContextPressureFlags:\n-    \"\"\"Context pressure warning flag tracking on AIAgent.\"\"\"\n-\n-    def test_flag_initialized_zero(self, agent):\n-        assert agent._context_pressure_warned_at == 0.0\n-\n-    def test_emit_calls_status_callback(self, agent):\n-        \"\"\"status_callback should be invoked with event type and message.\"\"\"\n-        cb = MagicMock()\n-        agent.status_callback = cb\n-\n-        compressor = MagicMock()\n-        compressor.context_length = 200_000\n-        compressor.threshold_tokens = [redacted:secret-assignment]  # 50%\n-\n-        agent._emit_context_pressure(0.85, compressor)\n-\n-        cb.assert_called_once()\n-        args = cb.call_args[0]\n-        assert args[0] == \"context_pressure\"\n-        assert \"85% to compaction\" in args[1]\n-\n-    def test_emit_no_callback_no_crash(self, agent):\n-        \"\"\"No status_callback set — should not crash.\"\"\"\n-        agent.status_callback = None\n-\n-        compressor = MagicMock()\n-        compressor.context_length = 200_000\n-        compressor.threshold_tokens = [redacted:secret-assignment]\n-\n-        # Should not raise\n-        agent._emit_context_pressure(0.60, compressor)\n-\n-    def test_emit_prints_for_cli_platform(self, agent, capsys):\n-        \"\"\"CLI platform should always print context pressure, even in quiet_mode.\"\"\"\n-        agent.quiet_mode = True\n-        agent.platform = \"cli\"\n-        agent.status_callback = None\n-\n-        compressor = MagicMock()\n-        compressor.context_length = 200_000\n-        compressor.threshold_tokens = [redacted:secret-assignment]\n-\n-        agent._emit_context_pressure(0.85, compressor)\n-        captured = capsys.readouterr()\n-        assert \"▰\" in captured.out\n-        assert \"to compaction\" in captured.out\n-\n-    def test_emit_skips_print_for_gateway_platform(self, agent, capsys):\n-        \"\"\"Gateway platforms get the callback, not CLI print.\"\"\"\n-        agent.platform = \"telegram\"\n-        agent.status_callback = None\n-\n-        compressor = MagicMock()\n-        compressor.context_length = 200_000\n-        compressor.threshold_tokens = [redacted:secret-assignment]\n-\n-        agent._emit_context_pressure(0.85, compressor)\n-        captured = capsys.readouterr()\n-        assert \"▰\" not in captured.out\n-\n-    def test_flag_reset_on_compression(self, agent):\n-        \"\"\"After _compress_context, context pressure flag should reset.\"\"\"\n-        agent._context_pressure_warned_at = 0.85\n-        agent.compression_enabled = True\n-\n-        agent.context_compressor = MagicMock()\n-        agent.context_compressor.compress.return_value = [\n-            {\"role\": \"user\", \"content\": \"Summary of conversation so far.\"}\n-        ]\n-        agent.context_compressor.context_length = 200_000\n-        agent.context_compressor.threshold_tokens = [redacted:secret-assignment]\n-        agent.context_compressor.compression_count = 1\n-\n-        agent._todo_store = MagicMock()\n-        agent._todo_store.format_for_injection.return_value = None\n-\n-        agent._build_system_prompt = MagicMock(return_value=\"system prompt\")\n-        agent._cached_system_prompt = \"old system prompt\"\n-        agent._session_db = None\n-\n-        messages = [\n-            {\"role\": \"user\", \"content\": \"hello\"},\n-            {\"role\": \"assistant\", \"content\": \"hi there\"},\n-        ]\n-        agent._compress_context(messages, \"system prompt\")\n-\n-        assert agent._context_pressure_warned_at == 0.0\n-\n-    def test_emit_callback_error_handled(self, agent):\n-        \"\"\"If status_callback raises, it should be caught gracefully.\"\"\"\n-        cb = MagicMock(side_effect=RuntimeError(\"callback boom\"))\n-        agent.status_callback = cb\n-\n-        compressor = MagicMock()\n-        compressor.context_length = 200_000\n-        compressor.threshold_tokens = [redacted:secret-assignment]\n-\n-        # Should not raise\n-        agent._emit_context_pressure(0.85, compressor)\n-\n-    def test_tiered_reemits_at_95(self, agent):\n-        \"\"\"Warning fires at 85%, then fires again when crossing 95%.\"\"\"\n-        agent._context_pressure_warned_at = 0.85\n-        # Simulate crossing 95%: the tier (0.95) > warned_at (0.85)\n-        assert 0.95 > agent._context_pressure_warned_at\n-        # After emission at 95%, the tier should update\n-        agent._context_pressure_warned_at = 0.95\n-        assert agent._context_pressure_warned_at == 0.95\n-\n-    def test_tiered_no_double_emit_at_same_level(self, agent):\n-        \"\"\"Once warned at 85%, further 85%+ readings don't re-warn.\"\"\"\n-        agent._context_pressure_warned_at = 0.85\n-        # At 88%, tier is 0.85, which is NOT > warned_at (0.85)\n-        _warn_tier = 0.85 if 0.88 >= 0.85 else 0.0\n-        assert not (_warn_tier > agent._context_pressure_warned_at)\n-\n-    def test_flag_not_reset_when_compression_insufficient(self, agent):\n-        \"\"\"When compression can't drop below 85%, keep the flag set.\"\"\"\n-        agent._context_pressure_warned_at = 0.85\n-        agent.compression_enabled = True\n-\n-        agent.context_compressor = MagicMock()\n-        agent.context_compressor.compress.return_value = [\n-            {\"role\": \"user\", \"content\": \"Summary of conversation so far.\"}\n-        ]\n-        agent.context_compressor.context_length = 200\n-        # Use a small threshold so the tiny compressed output still\n-        # represents >= 85% of it (prevents flag reset).\n-        agent.context_compressor.threshold_tokens = 10\n-        agent.context_compressor.compression_count = 1\n-        agent.context_compressor.last_prompt_tokens = 0\n-\n-        agent._todo_store = MagicMock()\n-        agent._todo_store.format_for_injection.return_value = None\n-        agent._build_system_prompt = MagicMock(return_value=\"system prompt\")\n-        agent._cached_system_prompt = \"old system prompt\"\n-        agent._session_db = None\n-\n-        messages = [\n-            {\"role\": \"user\", \"content\": \"hello\"},\n-            {\"role\": \"assistant\", \"content\": \"hi there\"},\n-        ]\n-        agent._compress_context(messages, \"system prompt\")\n-\n-        # Post-compression is ~90% of threshold — flag should NOT reset\n-        assert agent._context_pressure_warned_at == 0.85\n-\n-\n-class TestContextPressureGatewayDedup:\n-    \"\"\"Class-level dedup prevents warning spam across AIAgent instances.\"\"\"\n-\n-    def setup_method(self):\n-        \"\"\"Clear class-level dedup state between tests.\"\"\"\n-        AIAgent._context_pressure_last_warned.clear()\n-\n-    def test_second_instance_within_cooldown_suppressed(self):\n-        \"\"\"Same session, same tier, within cooldown — should be suppressed.\"\"\"\n-        import time\n-        sid = \"test_session_dedup\"\n-        # Simulate first warning\n-        AIAgent._context_pressure_last_warned[sid] = (0.85, time.time())\n-        # Second instance checking same tier within cooldown\n-        _last = AIAgent._context_pressure_last_warned.get(sid)\n-        _should_warn = _last is None or _last[0] < 0.85 or (time.time() - _last[1]) >= AIAgent._CONTEXT_PRESSURE_COOLDOWN\n-        assert not _should_warn\n-\n-    def test_higher_tier_fires_despite_cooldown(self):\n-        \"\"\"Same session, higher tier — should fire even within cooldown.\"\"\"\n-        import time\n-        sid = \"test_session_tier\"\n-        AIAgent._context_pressure_last_warned[sid] = (0.85, time.time())\n-        _last = AIAgent._context_pressure_last_warned.get(sid)\n-        # 0.95 > 0.85 stored tier → should warn\n-        _should_warn = _last is None or _last[0] < 0.95 or (time.time() - _last[1]) >= AIAgent._CONTEXT_PRESSURE_COOLDOWN\n-        assert _should_warn\n-\n-    def test_warning_fires_after_cooldown_expires(self):\n-        \"\"\"Same session, same tier, after cooldown — should fire again.\"\"\"\n-        import time\n-        sid = \"test_session_expired\"\n-        # Set a timestamp far in the past\n-        AIAgent._context_pressure_last_warned[sid] = (0.85, time.time() - AIAgent._CONTEXT_PRESSURE_COOLDOWN - 1)\n-        _last = AIAgent._context_pressure_last_warned.get(sid)\n-        _should_warn = _last is None or _last[0] < 0.85 or (time.time() - _last[1]) >= AIAgent._CONTEXT_PRESSURE_COOLDOWN\n-        assert _should_warn\n-\n-    def test_compression_clears_dedup(self):\n-        \"\"\"After compression drops below 85%, dedup entry should be cleared.\"\"\"\n-        import time\n-        sid = \"test_session_clear\"\n-        AIAgent._context_pressure_last_warned[sid] = (0.85, time.time())\n-        assert sid in AIAgent._context_pressure_last_warned\n-        # Simulate what _compress_context does on reset\n-        AIAgent._context_pressure_last_warned.pop(sid, None)\n-        assert sid not in AIAgent._context_pressure_last_warned\n-\n-    def test_eviction_removes_stale_entries(self):\n-        \"\"\"Stale entries older than 2x cooldown should be evicted.\"\"\"\n-        import time\n-        _now = time.time()\n-        AIAgent._context_pressure_last_warned = {\n-            \"fresh\": (0.85, _now),\n-            \"stale\": (0.85, _now - AIAgent._CONTEXT_PRESSURE_COOLDOWN * 3),\n-        }\n-        _cutoff = _now - AIAgent._CONTEXT_PRESSURE_COOLDOWN * 2\n-        AIAgent._context_pressure_last_warned = {\n-            k: v for k, v in AIAgent._context_pressure_last_warned.items()\n-            if v[1] > _cutoff\n-        }\n-        assert \"fresh\" in AIAgent._context_pressure_last_warned\n-        assert \"stale\" not in AIAgent._context_pressure_last_warned\n"]}]}],"trust":{"default_tier":"verified","includes_suggested":false}}