Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions agent/display.py
Original file line number Diff line number Diff line change
Expand Up @@ -699,7 +699,7 @@ def format_context_pressure(
threshold_percent: Compaction threshold as a fraction of context window.
compression_enabled: Whether auto-compression is active.
"""
pct_int = int(compaction_progress * 100)
pct_int = min(int(compaction_progress * 100), 100)
filled = min(int(compaction_progress * _BAR_WIDTH), _BAR_WIDTH)
bar = _BAR_FILLED * filled + _BAR_EMPTY * (_BAR_WIDTH - filled)

Expand Down Expand Up @@ -729,7 +729,7 @@ def format_context_pressure_gateway(
No ANSI — just Unicode and plain text suitable for Telegram/Discord/etc.
The percentage shows progress toward the compaction threshold.
"""
pct_int = int(compaction_progress * 100)
pct_int = min(int(compaction_progress * 100), 100)
filled = min(int(compaction_progress * _BAR_WIDTH), _BAR_WIDTH)
bar = _BAR_FILLED * filled + _BAR_EMPTY * (_BAR_WIDTH - filled)

Expand Down
23 changes: 11 additions & 12 deletions run_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -7250,7 +7250,6 @@ def _stop_spinner():
except Exception:
pass

_msg_count_before_tools = len(messages)
self._execute_tool_calls(assistant_message, messages, effective_task_id, api_call_count)

# Signal that a paragraph break is needed before the next
Expand All @@ -7268,18 +7267,18 @@ def _stop_spinner():
if _tc_names == {"execute_code"}:
self.iteration_budget.refund()

# Estimate next prompt size using real token counts from the
# last API response + rough estimate of newly appended tool
# results. This catches cases where tool results push the
# context past the limit that last_prompt_tokens alone misses
# (e.g. large file reads, web extractions).
# Use real token counts from the API response to decide
# compression. prompt_tokens + completion_tokens is the
# actual context size the provider reported plus the
# assistant turn — a tight lower bound for the next prompt.
# Tool results appended above aren't counted yet, but the
# threshold (default 50%) leaves ample headroom; if tool
# results push past it, the next API call will report the
# real total and trigger compression then.
_compressor = self.context_compressor
_new_tool_msgs = messages[_msg_count_before_tools:]
_new_chars = sum(len(str(m.get("content", "") or "")) for m in _new_tool_msgs)
_estimated_next_prompt = (
_real_tokens = (
_compressor.last_prompt_tokens
+ _compressor.last_completion_tokens
+ _new_chars // 3 # conservative: JSON-heavy tool results ≈ 3 chars/token
)

# ── Context pressure warnings (user-facing only) ──────────
Expand All @@ -7289,12 +7288,12 @@ def _stop_spinner():
# Does not inject into messages — just prints to CLI output
# and fires status_callback for gateway platforms.
if _compressor.threshold_tokens > 0:
_compaction_progress = _estimated_next_prompt / _compressor.threshold_tokens
_compaction_progress = _real_tokens / _compressor.threshold_tokens
if _compaction_progress >= 0.85 and not self._context_pressure_warned:
self._context_pressure_warned = True
self._emit_context_pressure(_compaction_progress, _compressor)

if self.compression_enabled and _compressor.should_compress(_estimated_next_prompt):
if self.compression_enabled and _compressor.should_compress(_real_tokens):
messages, active_system_prompt = self._compress_context(
messages, system_message,
approx_tokens=self.context_compressor.last_prompt_tokens,
Expand Down
11 changes: 10 additions & 1 deletion tests/test_context_pressure.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,12 @@ def test_returns_string(self):
assert isinstance(result, str)

def test_over_100_percent_capped(self):
"""Progress > 1.0 should not break the bar."""
"""Progress > 1.0 should cap both bar and percentage text at 100%."""
line = format_context_pressure(1.05, 100_000, 0.50)
assert "▰" in line
assert line.count("▰") == 20
assert "100%" in line
assert "105%" not in line


class TestFormatContextPressureGateway:
Expand Down Expand Up @@ -100,6 +102,13 @@ def test_has_progress_bar(self):
msg = format_context_pressure_gateway(0.80, 0.50)
assert "▰" in msg

def test_over_100_percent_capped(self):
"""Progress > 1.0 should cap percentage text at 100%."""
msg = format_context_pressure_gateway(1.09, 0.50)
assert "100% to compaction" in msg
assert "109%" not in msg
assert msg.count("▰") == 20


# ---------------------------------------------------------------------------
# AIAgent context pressure flag tests
Expand Down
Loading