Skip to content

fix(agent): persist compression backoff across resume (#54465) - #54525

Closed
rodboev wants to merge 7 commits into
NousResearch:mainfrom
rodboev:pr/agent-compression-session-safety
Closed

fix(agent): persist compression backoff across resume (#54465)#54525
rodboev wants to merge 7 commits into
NousResearch:mainfrom
rodboev:pr/agent-compression-session-safety

Conversation

@rodboev

@rodboev rodboev commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Summary

Resumed oversized sessions can currently re-enter automatic preflight compression immediately after a timeout because the cooldown lives only on the in-memory ContextCompressor. A restart builds a fresh compressor, so the next turn can hit the same preflight compression path before any assistant response.

This PR persists same-session compression failure state on the session row, hydrates that state into resumed compressors, rebinds reset-only session switches onto the active session row, suppresses only same-session automatic preflight while the cooldown is still live, and keeps the current compression lock lease valid while the owning compression is still in flight. Manual /compress keeps its existing bypass behavior, and unrelated session IDs stay unaffected.

Related scope note: #53171, #49905, and #41397 already cover the timeout-retry and wall-clock slices from this issue, so this PR stays on the remaining durable session-state piece.

Changes

  • hermes_state.py: add session-scoped compression failure columns and SessionDB helpers for record/read/clear plus owner-checked lock lease refresh.
  • agent/context_compressor.py: bind resumed session state into the built-in compressor, write through same-session cooldown changes, and preserve manual force bypass while keeping restart-persistent cooldowns intact.
  • agent/agent_init.py, run_agent.py: bind fresh or reset-only session switches onto the active session row so later cooldown reads and writes stay on the right session after /new, /resume, or /branch.
  • agent/turn_context.py: check same-session persisted cooldown before emitting the preflight compression status and entering the automatic preflight loop.
  • agent/conversation_compression.py: keep the current holder's lock lease alive until release, and release it on the remaining early exception exits after refresh starts.
  • tests/test_hermes_state.py, tests/agent/test_context_compressor.py, tests/agent/test_context_engine_host_contract.py, tests/agent/test_turn_context.py, tests/agent/test_compression_concurrent_fork.py: add focused regression coverage for persisted cooldown behavior, session isolation, reset-only session rebinding, owner-only lease refresh, and negative-space cases.

Validation

Scenario Before After
Resume the same oversized session after a compression timeout fresh process forgets cooldown and auto preflight retries immediately same-session cooldown survives restart and skips auto preflight until it expires or is bypassed
Run manual /compress during an active cooldown force bypass works only through the in-memory field in the current process force bypass still works and clears the same-session persisted cooldown too
Switch to /new, /resume, or /branch after a session-specific cooldown reset-only session switches can keep reading or writing the previous session row reset-only session switches rebind the built-in compressor before later cooldown reads or writes
Different session is over threshold while another session has a live cooldown no durable per-session guard exists other session still compresses normally because cooldown state is keyed by exact session ID
Compression runs longer than the original 300s lease current holder's row can expire mid-flight owner-checked refresh keeps the lease live until release
Crashed owner leaves a stale expired lock stale lock is reclaimable stale lock remains reclaimable after refresh stops

Test plan

  • Windows conhost-headless pytest wrapper from repo config with tests/agent/test_context_engine_host_contract.py tests/test_hermes_state.py tests/agent/test_context_compressor.py tests/agent/test_turn_context.py tests/agent/test_compression_concurrent_fork.py -v --timeout=0 — 453 passed

Upstream

This PR intentionally leaves #54465 open because the timeout-retry and wall-clock slices are tracked separately in #53171, #49905, and #41397.

@alt-glitch alt-glitch added type/bug Something isn't working comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P2 Medium — degraded but workaround exists sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state labels Jun 29, 2026
@kshitijk4poor

Copy link
Copy Markdown
Collaborator

Salvaged and merged via #55499 (merge commit 58d8e25 on main).

Your six commits were cherry-picked unchanged, so your authorship is preserved per-commit in main's history. Thank you for the thorough fix and the excellent repro/evidence in #54465 — the cross-process restart wedge was exactly as you diagnosed.

I added one follow-up commit on top hardening the lock-lease refresher: its loop previously treated any falsy refresh as a permanent stop, which conflated genuine lost-ownership with a one-off transient DB error — a single blip could silently reintroduce the TTL-expiry wedge. It now tolerates consecutive failures for at most one lease's worth of time (cap = int(ttl / refresh_interval), floor 1), so the give-up window is genuinely bounded by the TTL, plus debug logging on the two previously-silent cooldown except arms and 5 mutation-checked regression tests.

Closing this in favor of the merged #55499. Thanks again!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P2 Medium — degraded but workaround exists sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants