Skip to content

fix(context_compressor): always keep last user message in tail to prevent active-task loss (#10896)#10969

Closed
sontianye wants to merge 1 commit into
NousResearch:mainfrom
sontianye:fix/context-compression-active-task-loss-10896
Closed

fix(context_compressor): always keep last user message in tail to prevent active-task loss (#10896)#10969
sontianye wants to merge 1 commit into
NousResearch:mainfrom
sontianye:fix/context-compression-active-task-loss-10896

Conversation

@sontianye

Copy link
Copy Markdown

Problem

Fixes #10896.

When context compression fires mid-task, the agent can silently drop the user's current request and either stall or re-execute a previously completed task.

Root cause

_find_tail_cut_by_tokens finishes with a call to _align_boundary_backward, which walks the cut boundary backward to avoid splitting a tool_call/tool_result group. In pathological cases this slides cut_idx past the most recent user message, pushing it into the compressed (middle) region.

The LLM summariser faithfully records that user message in ## Pending User Asks, but SUMMARY_PREFIX instructs the receiving model:

"Respond ONLY to the latest user message that appears AFTER this summary."

With the user message now inside the summary rather than in the live tail, the model has no post-summary user message to act on. It either stalls or — worse — interprets the summary's ## Completed Actions as still-pending work and re-executes it.

Reproduction (from issue):

  1. Task A completes (many tool calls, large context).
  2. User issues Task B.
  3. Compression fires while Task B is in its first tool-call cycle.
  4. _align_boundary_backward pulls cut_idx before user: "Task B".
  5. Agent resumes, ignores Task B, and re-executes Task A.

Fix

1. Structural fix — _find_tail_cut_by_tokens

Added two new private methods:

_find_last_user_message_idx(messages, head_end)
Scans backward from the end to find the last user-role message index.

_ensure_last_user_message_in_tail(messages, cut_idx, head_end)
If the last user message is already in the tail (>= cut_idx), does nothing. If it fell into the middle region, sets cut_idx = last_user_idx directly — a user message is a clean exchange boundary with no tool_call/tool_result pair straddling it, so calling _align_boundary_backward again would over-correct.

Called as the final step of _find_tail_cut_by_tokens, after the existing _align_boundary_backward call:

# Align to avoid splitting tool groups
cut_idx = self._align_boundary_backward(messages, cut_idx)

# Ensure the most recent user message is always in the tail so the
# active task is never lost to compression (fixes #10896).
cut_idx = self._ensure_last_user_message_in_tail(messages, cut_idx, head_end)

2. Summary-template defence-in-depth

Even with the structural fix in place, iterative re-compressions over very long sessions could still lose task context. Three template-level changes add a second layer of protection:

New ## Active Task section (top of template, highest priority):

## Active Task
[THE SINGLE MOST IMPORTANT FIELD. Copy the user's most recent request or
task assignment verbatim — the exact words they used. ...]

Updated SUMMARY_PREFIX — explicitly points the receiving model to ## Active Task so it knows where to find its current objective.

Updated iterative-update prompt — adds CRITICAL: Update "## Active Task" to the list of things to maintain across re-compressions.

Impact

  • Zero behaviour change when the last user message already sits inside the token-budget tail (the common case — _ensure_last_user_message_in_tail returns immediately).
  • Fixes the regression when _align_boundary_backward over-corrects by anchoring cut_idx to the last user message.
  • The tail may grow slightly larger than the token budget when anchoring fires, but this is bounded (at most one additional user message) and preferred over silent task loss.

…vent task loss (NousResearch#10896)

When `_align_boundary_backward` tried to keep tool_call/result groups
intact it could inadvertently slide the tail boundary past the most
recent user message, pushing it into the compressed (middle) region.
The summariser would then record that user message in "Pending User
Asks", but SUMMARY_PREFIX tells the receiving model to respond only to
user messages *after* the summary — so the active task silently
disappeared, causing the agent to stall, repeat completed work, or
re-execute an already-finished Task A instead of continuing Task B.

Fix:
- Add `_find_last_user_message_idx()` helper.
- Add `_ensure_last_user_message_in_tail()`: if the last user message
  ended up in the compressed middle, pull `cut_idx` back to that index.
  A user message is a clean boundary (no tool-pair splitting risk) so
  we set `cut_idx = last_user_idx` directly rather than calling
  `_align_boundary_backward` again, which would over-correct.
- Call `_ensure_last_user_message_in_tail()` at the end of
  `_find_tail_cut_by_tokens()`, after the existing
  `_align_boundary_backward` call, as a final safety pass.

Summary-template improvements (defence-in-depth):
- Add `## Active Task` section at the top of the structured template
  with explicit instruction to copy the user's latest unfulfilled
  request verbatim — giving the next model a clear, prominent anchor.
- Update SUMMARY_PREFIX to point readers to the `## Active Task`
  section so they know exactly where to find their current objective.
- Add "CRITICAL: Update '## Active Task'" reminder to the iterative
  (re-compression) update prompt.

Fixes NousResearch#10896
kshitijk4poor pushed a commit that referenced this pull request Apr 16, 2026
…vent active-task loss

Ensure _align_boundary_backward never pushes the last user message
into the compressed region. Without this, compression could delete
the user active task instruction mid-session.

Cherry-picked from #10969 by @sontianye. Fixes #10896.
@kshitijk4poor

Copy link
Copy Markdown
Collaborator

Salvaged into #11052. Cherry-picked with authorship preserved onto current main. All 44 compressor tests pass. Thanks @sontianye!

kshitijk4poor pushed a commit that referenced this pull request Apr 16, 2026
…vent active-task loss

Ensure _align_boundary_backward never pushes the last user message
into the compressed region. Without this, compression could delete
the user active task instruction mid-session.

Cherry-picked from #10969 by @sontianye. Fixes #10896.
kshitijk4poor pushed a commit that referenced this pull request Apr 16, 2026
…vent active-task loss

Ensure _align_boundary_backward never pushes the last user message
into the compressed region. Without this, compression could delete
the user active task instruction mid-session.

Cherry-picked from #10969 by @sontianye. Fixes #10896.
ulasbilgen pushed a commit to ulasbilgen/hermes-adhd-agent that referenced this pull request May 1, 2026
…vent active-task loss

Ensure _align_boundary_backward never pushes the last user message
into the compressed region. Without this, compression could delete
the user active task instruction mid-session.

Cherry-picked from NousResearch#10969 by @sontianye. Fixes NousResearch#10896.
aj-nt pushed a commit to aj-nt/hermes-agent that referenced this pull request May 1, 2026
…vent active-task loss

Ensure _align_boundary_backward never pushes the last user message
into the compressed region. Without this, compression could delete
the user active task instruction mid-session.

Cherry-picked from NousResearch#10969 by @sontianye. Fixes NousResearch#10896.
02356abc pushed a commit to 02356abc/hermes-agent that referenced this pull request May 14, 2026
…vent active-task loss

Ensure _align_boundary_backward never pushes the last user message
into the compressed region. Without this, compression could delete
the user active task instruction mid-session.

Cherry-picked from NousResearch#10969 by @sontianye. Fixes NousResearch#10896.
gweeteve pushed a commit to gweeteve/hermes-agent that referenced this pull request Jun 2, 2026
…vent active-task loss

Ensure _align_boundary_backward never pushes the last user message
into the compressed region. Without this, compression could delete
the user active task instruction mid-session.

Cherry-picked from NousResearch#10969 by @sontianye. Fixes NousResearch#10896.
Egavasyug pushed a commit to Egavasyug/hermes-agent that referenced this pull request Jun 10, 2026
…vent active-task loss

Ensure _align_boundary_backward never pushes the last user message
into the compressed region. Without this, compression could delete
the user active task instruction mid-session.

Cherry-picked from NousResearch#10969 by @sontianye. Fixes NousResearch#10896.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Context compression causes task regression — agent reverts to executing an earlier completed task after context is compacted

2 participants