Skip to content

fix(compression): add deterministic handoff for fallback compaction#26189

Closed
carltonawong wants to merge 1 commit into
NousResearch:mainfrom
carltonawong:fix/compression-deterministic-fallback-handoff
Closed

fix(compression): add deterministic handoff for fallback compaction#26189
carltonawong wants to merge 1 commit into
NousResearch:mainfrom
carltonawong:fix/compression-deterministic-fallback-handoff

Conversation

@carltonawong

Copy link
Copy Markdown
Contributor

Summary

Adds a deterministic pre-drop handoff for the summary-failure fallback path in ContextCompressor.

When the LLM summary cannot be generated but Hermes still compacts a middle window, the fallback summary now includes bounded, redacted source context from the exact removed turns instead of only a generic “summary unavailable” marker.

Problem

Failed summary compression currently preserves the fact that context was lost, but not the actionable state needed to continue safely.

That matters in long-running gateway sessions: durable memory and recent tail context may still exist, but the removed middle window can contain the active workflow trigger, tool state, file/path references, or the last concrete user ask. A generic fallback marker tells the next model that something was dropped, but gives it little evidence for correct continuation.

Skipping a failed compression attempt is safest when the full message list can still be retained. This patch handles the lossy fallback case where Hermes is already compacting a middle window and needs a non-LLM continuity receipt from the exact dropped span.

Changes

  • agent/context_compressor.py
    • adds a bounded deterministic fallback-handoff builder;
    • captures recent user requests, tool-call names, tool/result text, file/path mentions, and last dropped turns;
    • runs existing secret redaction before fallback text enters the compacted context;
    • appends the handoff only on the failed-summary fallback path.
  • tests/agent/test_context_compressor.py
    • strengthens the existing summary-failure regression to require an actionable fallback handoff;
    • covers tool-call/path preservation;
    • covers secret redaction;
    • covers bounded/truncated fallback output.

Relationship to existing compression fixes

Related to #25585 and the fallback-marker/context-loss reports such as #16670.

This is complementary to PRs like #26051 / #25588 that preserve the original message list when failed compression can be skipped safely. That is the preferred behavior when the full context can still be retained.

This PR covers the remaining hard fallback case: if Hermes is going to replace a middle window anyway, the replacement should contain deterministic source evidence from the dropped span, not just a generic marker.

Invariant this moves toward:

No dropped context window without either a real summary, a native compaction artifact, preserved original messages, or a deterministic pre-drop handoff.

Validation

python -m pytest -o addopts='' tests/agent/test_context_compressor.py::TestSummaryFailureTrackingForGatewayWarning -q
# 4 passed

python -m pytest -o addopts='' tests/agent/test_context_compressor.py -q
# 82 passed

python -m py_compile agent/context_compressor.py tests/agent/test_context_compressor.py
git diff --check

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✅ Tests (adding or improving test coverage)
  • ✨ New feature (non-breaking change that adds functionality)
  • 🔒 Security fix
  • 📝 Documentation update
  • ♻️ Refactor (no behavior change)
  • 🎯 New skill (bundled or hub)

Checklist

Code

  • My commit messages follow Conventional Commits
  • I searched for existing PRs to make sure this is not a duplicate; this is scoped as the hard-fallback handoff layer rather than the fail-closed preserve-original layer
  • My PR contains only changes related to this fix
  • I added tests for the change
  • I tested on Linux/WSL

Documentation & Housekeeping

  • Documentation update N/A — behavior is covered by tests and inline comments
  • cli-config.yaml.example N/A — no config keys changed
  • Tool descriptions/schemas N/A — no tool schemas changed

@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/agent Core agent loop, run_agent.py, prompt builder labels May 15, 2026
NishantEC

This comment was marked as outdated.

@carltonawong carltonawong force-pushed the fix/compression-deterministic-fallback-handoff branch from 9e7833a to 84f1df8 Compare May 19, 2026 18:01
@carltonawong

Copy link
Copy Markdown
Contributor Author

Updated this branch on top of current main and resolved the compressor conflict by keeping both paths:

  • compression.abort_on_summary_failure=True still preserves the original messages and aborts compression, matching the new fail-closed behavior on main.
  • The default fallback path still compacts, but now uses a bounded deterministic pre-compaction handoff from the exact dropped window instead of only a static "summary unavailable" marker.

So this PR is intended to complement the new config flag: when callers choose the safest preservation mode, nothing is dropped; when they keep the default fallback behavior or need compaction to proceed under context pressure, the dropped window now leaves actionable, redacted context behind.

Validated after rebasing:

python -m pytest -o addopts='' tests/agent/test_context_compressor.py -q

Result: 85 passed.

@carltonawong carltonawong force-pushed the fix/compression-deterministic-fallback-handoff branch from 84f1df8 to 759f544 Compare May 27, 2026 01:07
@carltonawong

Copy link
Copy Markdown
Contributor Author

Refreshed this branch on top of current main.

The PR is still intentionally narrow: it only changes the summary-failure fallback path in ContextCompressor, preserving the existing fail-closed abort_on_summary_failure=True behavior while improving the default lossy fallback with a bounded deterministic handoff from the dropped window.

Validated after rebasing:

python -m pytest -o addopts='' tests/agent/test_context_compressor.py -q

Result: 85 passed, 1 warning.

Would appreciate a maintainer review when someone has bandwidth. I think this is complementary to the existing compression safety work because it covers the case where Hermes proceeds with compaction after summary generation fails.

@carltonawong

Copy link
Copy Markdown
Contributor Author

Looks like this behavior has now landed via #34310 / #31242. Closing this as superseded — glad the deterministic fallback path made it into main.

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

Labels

comp/agent Core agent loop, run_agent.py, prompt builder P2 Medium — degraded but workaround exists type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants