Skip to content
UHPUHPDeveloper Guide
Independent resource · Not affiliated with HarnessRouter · Site data checked 19 Sep 2026

Agent runtime

Context Compaction Control in Agent Harnesses

Long-context agent reliability depends on more than deciding when to compact. A robust harness separately controls the compaction trigger, the amount of recent context protected after compaction, the continuity anchors that may exceed that budget, and the boundary between rewritten history and an already-dispatched tool call.

Verified: Evidence: Hermes PRs #115986, #115987, #115988Protocol: 2026-09-12

Context compaction is a two-budget control problem plus a provenance boundary. The harness needs one policy for when compaction starts and another for how much recent material survives it. It must then preserve required conversational and tool-call continuity without pretending those requirements are hard token limits. Finally, any pruning or truncation applied to stored history must remain distinct from the payload of a tool call that was already dispatched for execution.

Hermes PRs #115986, #115987 and #115988, merged on 19 September 2026, provide a useful current implementation example. PR #115986 places a default absolute ceiling of 256,000 tokens on the compression trigger for large-context models. PR #115987 prevents the recent-message count floor from expanding the optional protected tail beyond its 1.5× soft token ceiling. PR #115988 clarifies an important execution boundary: pressure pruning can rewrite the history copy of recent tool arguments/results that the model will read later, but it does not alter the arguments of an already-dispatched tool call.

These are post-release Hermes runtime changes. The latest stable Hermes release remains v2026.9.14 / Agent v0.21.3; checked main after #115986 is 7c6f21a5e12ba9b1c674ec9b410fa6b8c45de4f8. None of these changes revises UHP, MCP, ACP or A2A wire semantics, establishes native Hermes UHP adoption, or constitutes fresh HarnessRouter Hermes conformance evidence.

Trigger budget: ratio alone is not enough for very large windows

Section titled “Trigger budget: ratio alone is not enough for very large windows”

A ratio-based trigger scales naturally with the model context window, but that can become undesirable once context windows reach hundreds of thousands or millions of tokens. A 50% threshold on a 1,000,000-token window waits until roughly 500,000 tokens before compaction begins. That can create very large repeated input prefixes, long recovery/replay surfaces and substantial token cost before the first compaction occurs.

PR #115986 changes Hermes’ shipped default compression.threshold_tokens from null to 256000. The effective trigger is now the lower of the ratio-derived threshold and the absolute cap. Explicit null keeps ratio-only behavior.

Conceptually:

effective_trigger = min(ratio_trigger, absolute_cap)

The important design property is first-fires-wins. The absolute cap should not delay a model whose proportional trigger is already lower. Hermes’ own examples retain a 96K trigger on a 128K window and about 204K on the referenced 272K Codex window, while a 1M window that previously reached 500K now compacts at 256K. Upstream characterizes the behavioral change as applying only once the normal ratio trigger rises above the cap, roughly above a 341K window under the referenced defaults.

This separates two operator intents that are easy to conflate:

  • a ratio expresses how much of a model’s available context the operator is willing to consume before compaction;
  • an absolute ceiling bounds worst-case context growth across models with very different windows.

A model switch must recompute both parts rather than carry forward a threshold derived for the previous model.

Diagnostics must report the trigger the runtime will actually use

Section titled “Diagnostics must report the trigger the runtime will actually use”

A safety or cost warning is misleading if it reports window × percentage while the runtime applies a lower cap, a model-specific override or a small-window floor. PR #115986 therefore routes the model-switch preview and the compressor’s installed threshold through one shared trigger derivation.

The general invariant is:

The number shown to an operator should be derived by the same policy function that installs the runtime limit.

This avoids a class of control-plane drift where configuration, warning text and actual runtime behavior each implement a slightly different formula. The same rule applies to admission limits, timeouts, retry budgets and authorization previews elsewhere in an agent host.

Tail budget: message count is not a token guarantee

Section titled “Tail budget: message count is not a token guarantee”

After compaction starts, a harness usually preserves a recent tail so the model retains the current conversation and tool context. A simple protect_last_n rule is not enough: eight recent messages can be tiny, or each can contain thousands of tokens.

Before PR #115987, Hermes’ lean-tail selector could calculate a token-bounded cut and then unconditionally widen the result to satisfy the recent-message count floor. In the reported failure, protected_tail_tokens reached 54,818 against a 10,000-token budget. That incompressible tail consumed about 40% of the compression trigger.

The fix makes the message-count floor opportunistic. If the newest optional rows fit under the tail’s 1.5× soft ceiling, the count floor can preserve them. If they do not, the token-bounded cut wins.

For a 10,000-token tail budget, that means optional rows are normally held to a 15,000-token soft ceiling. Upstream’s production-shaped probe moved one oversized-assistant case from 35,050 tokens to 14,020 tokens. This is a much stronger invariant than saying “keep the last eight messages,” because the retained-state cost is now bounded by tokens rather than message cardinality.

Continuity anchors can legitimately exceed the soft ceiling

Section titled “Continuity anchors can legitimately exceed the soft ceiling”

The 1.5× ceiling is deliberately not a hard limit over every retained byte. PR #115987 preserves required conversational continuity after the optional-tail cut. The last relevant user/assistant anchors, multi-user continuity rules and atomic tool-call groups may still force the protected tail above the nominal ceiling.

That exception is important. A hard token slicer that breaks a tool-call/result pair or removes the active user request can produce an internally inconsistent transcript even though it meets a numeric budget.

The safe hierarchy is therefore:

  1. bound optional recent history by tokens;
  2. preserve required conversation anchors;
  3. preserve tool-call groups atomically;
  4. make any resulting ceiling overrun observable rather than claiming the budget was absolute.

Upstream’s probe demonstrates the distinction: an oversized last-user anchor remained at 28,051 tokens against the 15K soft ceiling because continuity won by design. That is not the same failure as optional history expanding without a bound.

Pressure pruning may rewrite protected history

Section titled “Pressure pruning may rewrite protected history”

“Protected tail” is also easy to over-interpret. PR #115988 corrects Hermes documentation to state that its pressure pass may truncate tool-call arguments and demote tool results inside the protected tail when that tail alone exceeds its pressure budget. That pass runs as part of compaction independently of the proactive-pruning option described by the upstream docs.

The durable lesson is that a protection label needs a precise object and guarantee. A harness can protect a span from being summarized away while still allowing a later pressure stage to shorten large fields inside that span. Reviewers should ask:

  • Is the tail protected from deletion, from summarization, from field truncation, or from all three?
  • Which pass runs first when several budgets conflict?
  • Are truncation markers model-visible?
  • Can required atomic groups still be represented coherently after pressure reduction?

Without those answers, protect_last_n sounds stronger than the implementation actually is.

Rewritten history is not the same object as an executing tool call

Section titled “Rewritten history is not the same object as an executing tool call”

PR #115988 also records a particularly important harness boundary. Hermes executes a tool call from the provider response object before post-tool-result compaction rewrites the persisted/history representation. Therefore truncating arguments in the history copy does not retroactively modify the payload already delivered to the tool or delegated subagent.

This distinction is reusable beyond Hermes:

  • Execution object: the authoritative payload admitted for the current tool invocation.
  • History object: the representation later stored or reconstructed so the model can reason about what happened.
  • Replay object: a canonicalized representation used after restart/recovery, which must not silently redispatch an old side effect merely because a historical tool call is present.

A system that mutates one representation should not imply that the other two changed automatically. This is closely related to tool-call execution finality and session recovery: the model’s later transcript can be lossy or normalized while the execution record still needs enough provenance to say what actually ran.

The upstream documentation notes a separate model-level risk: once clipped JSON is visible in history, a model can imitate that clipped representation when constructing a new call. That is different from the original dispatched call having been altered in flight.

Delegated agents need the same budget semantics

Section titled “Delegated agents need the same budget semantics”

Compaction policy becomes harder in a parent/child harness. PR #115986 updates Hermes’ delegation comments so child agents without a child-specific cap compact where the parent policy says they should: the lower of the ratio trigger and the global compression.threshold_tokens cap. A dedicated child cap can narrow that further.

This suggests a general rule for delegated runtimes: a child should not silently escape a global resource ceiling merely because its context window is larger. At the same time, a child-specific lower cap is a policy choice, not evidence that every child should always compact sooner than its parent.

The relevant coordinates should remain explicit: model window, ratio policy, global cap, optional child cap and the actual installed trigger.

The evidence here is source-pinned upstream validation, not an independent benchmark by this site.

  • PR #115986: upstream’s final review note reports 1,356 tests across the 39 files that mention the touched trigger symbols passing after trigger derivation was centralized. Its broader earlier sweep reported 3,614 passes and seven SQLite failures that the author says reproduced on untouched upstream main.
  • PR #115987: upstream reports 178 targeted tests across five tail/boundary/pressure files, plus a 40,000-transcript randomized differential probe with zero mismatches for the final one-pass refactor relative to the reviewed two-walk logic.
  • PR #115988: this is documentation/source-trace evidence clarifying the ordering and object boundary; it is not a new runtime algorithm or benchmark.

Stable Hermes remains v0.21.3; these PRs are post-release current-main evidence. A future stable tag should be checked before describing the behavior as released.

Context compaction is primarily host/runtime policy. UHP can expose task/session lifecycle, streaming, artifacts and errors; MCP can expose tools/resources; ACP can connect an editor/client to an agent; A2A can coordinate agents. None of those facts, by themselves, standardizes how a harness should choose a compression threshold, preserve its recent tail or rewrite its local transcript.

An adapter may surface token usage or a runtime error caused by context pressure, but that does not make a local compaction algorithm a protocol conformance rule. Keep Hermes integration evidence separate from UHP behavior and from any HarnessRouter validation claim.

When reviewing context compaction in an agent harness, verify:

  1. Trigger policy is explicit: ratio thresholds, absolute caps and model-specific overrides have defined precedence.
  2. Preview equals execution: warnings and diagnostics use the same trigger derivation as the runtime.
  3. Model switches recompute policy: a threshold derived for one window is not reused blindly for another.
  4. Tail limits are token-aware: message count alone cannot override the optional-tail token budget without a documented reason.
  5. Soft exceptions are named: required conversation anchors and atomic tool groups may exceed a soft ceiling, and that overrun is observable.
  6. Protection semantics are precise: “protected” says whether deletion, summarization and field truncation are each allowed.
  7. Execution and history are distinct: rewriting stored history cannot retroactively change the payload already dispatched to a tool.
  8. Recovery is side-effect safe: canonicalized replay state must not cause old side effects to be redispatched accidentally.
  9. Delegation inherits bounded policy: child agents cannot escape an intended global ceiling without an explicit policy decision.
  10. Evidence stays versioned: current-main behavior is not described as stable-release or protocol behavior until primary sources establish that transition.