Skip to content

feat: tool truncation + auto-compaction to bash harness - #2454

Merged
mikasenghaas merged 17 commits into
mainfrom
feat/context-compaction
Aug 31, 2026
Merged

feat: tool truncation + auto-compaction to bash harness#2454
mikasenghaas merged 17 commits into
mainfrom
feat/context-compaction

Conversation

@mikasenghaas

@mikasenghaas mikasenghaas commented Aug 27, 2026

Copy link
Copy Markdown
Member

Summary

  • add an optional CompactionConfig to the bash harness: compact into a handoff summary at summarize_at_tokens, or when 16k tokens remain below the model context window
  • the program discovers the window itself from the provider's /models card (via models.list()), so the interception server gains a stateless GET /v1/models relay (one route serves every dialect)
  • truncate a tool result over 20KB to its head and tail with a warning naming the original size
  • compact reactively on an attributed 400/413 overflow error, from the current state; a rejected checkpoint falls back to the last state that passed a threshold check (which holds a full reserve of room by definition), and an empty or tool-calling reply is resampled
  • after three failed checkpoint attempts the program ends the run cleanly — still a trainable sample
  • an overflow with no history beyond the task propagates: nothing to reclaim

The RLM harness gets the same policy in #2459, stacked on this PR so this one can merge before nano-rlm does. Together with #2453 (merged) and #2459 this supersedes #2448.

Breaking

  • None on its own: compaction is a new optional field, unset by default.

Verification

  • uv run pytest -q tests/v1 — passed; live E2E tests skipped without PRIME_API_KEY

  • Tool truncation checked by exact match: a synthetic 55KB output produces the byte-identical expected head/tail + warning header; short outputs pass through untouched.
    Terminal-Bench 2 e2e: 8 tasks, local vLLM poolside/Laguna-XS-2.1 at 32k (glm45 reasoning + glm47 tool parsers), compaction = {} so the threshold auto-discovers to 32768 − 16384 = 16384. Trace analysis:

  • 8/8 rollouts end ok=true with scoring run (1 solved); no harness errors, no truncation stops.

  • The threshold triggers exactly where designed: 7/8 episodes compacted with peak contexts of 17.0k-23.4k tokens; the one episode that stayed at 15.4k never compacted.

  • 20KB tool truncation visible in 3 episodes' traces (Warning: truncated output with original size).

  • Laguna puts its entire checkpoint reply in the reasoning channel, so under the summaries-are-content-only rule every compaction on this model exercises the failure path end to end: three resampled checkpoint attempts, then the program ends the run cleanly - the rollout stays a trainable sample. Summary carry-over itself was demonstrated on models that answer in the content channel (Qwen3-0.6B at 4k/8k, deepseek-v4-flash on tb2): every non-final branch ends with the checkpoint prompt followed by the summary, and the next branch opens with [system, framed summary].

  • A final combined verification run on a content-channel model is pending.

🤖 Generated with Claude Code


Note

Medium Risk
The shared agent loop now permanently truncates large tool outputs and, when compaction is enabled, replaces long histories with model-generated summaries—both can change task outcomes; the new models relay is ancillary and failures only disable auto-thresholds.

Overview
Adds an opt-in context compaction path for the bash agent: BashHarnessConfig gains CompactionConfig (summarize_at_tokens optional), which forwards --compaction / --summarize-at-tokens into the shared minimal/bash chat program.

When compaction is on, the program uses a new Compactor to compact before context blows up: it can trigger on usage crossing a threshold (explicit token limit, or auto from the model card with a 16k reserve), on finish_reason=length, on post-tool estimated token growth, or reactively on provider 400/413 overflow. Compaction asks the model for a plain-text handoff summary (tool_choice=none), rebuilds history as system + framed summary user message, falls back to the last “good” checkpoint on overflow, and ends the loop cleanly on CompactionFailed instead of crashing. Threshold discovery calls models.list() against the intercepted base URL.

Tool results are always middle-truncated at ~20KB (head/tail + warning) via bound_tool_message before they enter the transcript, including interception rewrites.

The interception server adds GET /v1/models as an authenticated upstream relay (30s timeout) so the in-container SDK can read provider context-window fields without recording a model turn.

Reviewed by Cursor Bugbot for commit e1c80ac. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Add tool truncation and auto-compaction to BashHarness agent loop

  • Adds CompactionConfig to BashHarnessConfig and passes --compaction and --summarize-at-tokens flags to the spawned program in harness.py
  • Introduces the Compactor class in program.py that detects context exhaustion, builds a summary checkpoint, and rebuilds the conversation; auto-discovers a token threshold from the provider's /v1/models endpoint when none is given
  • Truncates large tool outputs to TOOL_OUTPUT_MAX_BYTES (20,000) with head/tail preservation via truncate_tool_output before they enter the conversation
  • Proxies GET /v1/models through the interception server in server.py so the program can query the provider for context window size
  • chat() now returns the full completion object instead of the first message, and the main loop ends cleanly on CompactionFailed
  • Risk: chat() signature and return type changed in program.py; any callers expecting the first message instead of the full completion will break. Tool outputs over 20,000 bytes are silently truncated mid-conversation.

Macroscope summarized 27e1c91.

@mikasenghaas
mikasenghaas marked this pull request as ready for review August 27, 2026 18:26
Comment thread verifiers/v1/interception/server.py Outdated
Comment thread verifiers/v1/harnesses/bash/program.py Outdated
@macroscopeapp

macroscopeapp Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This PR adds a stateful context-compaction workflow and a new provider-model relay, while unconditionally truncating large tool results in the shared Bash/Null loop. These changes can alter rollout context and completion behavior across production paths, so human review is appropriate.

You can add or adjust custom eligibility rules. Learn more.

Comment thread verifiers/v1/harnesses/bash/program.py Outdated
Comment thread verifiers/v1/harnesses/bash/program.py Outdated
Base automatically changed from feat/relay-overlong-errors to main August 27, 2026 19:43
@mikasenghaas mikasenghaas changed the title feat: add context compaction to the bash and rlm harnesses feat: feat: tool truncation + auto-compaction to bash harness Aug 27, 2026
@mikasenghaas
mikasenghaas force-pushed the feat/context-compaction branch from ca40cbf to fd382c5 Compare August 27, 2026 19:45
Comment thread verifiers/v1/harnesses/minimal/program.py
Comment thread verifiers/v1/harnesses/bash/program.py Outdated
@mikasenghaas mikasenghaas changed the title feat: feat: tool truncation + auto-compaction to bash harness feat: tool truncation + auto-compaction to bash harness Aug 27, 2026
Comment thread verifiers/v1/harnesses/minimal/program.py
samsja added a commit to PrimeIntellect-ai/nano-rlm that referenced this pull request Aug 28, 2026
## Summary

Hard-caps any single tool result entering the conversation at **10KB**,
middle-out (keep head+tail), with a warning header naming the original
size and line count:

```
Warning: truncated output (original token count: 12500)
Total output lines: 10001

<first 5KB>
[... 40000 bytes truncated ...]
<last 5KB>
```

Applies at the engine tool-result boundary — so it covers **all tools**
(`bash`, `edit`, and `ipython` cell output) uniformly.

## Scope: context only — tools yes, skills no

- The **session log keeps the full output** (audit trail unchanged).
- **Skill return values inside the kernel stay uncapped**: `out = await
bash(...)` holds the complete string for in-cell filtering; only what
the cell *prints* (the tool result) is subject to the cap.
Capture-then-filter workflows keep full fidelity — the cap only protects
the context window.

## Compatibility

The function is byte-compatible with the truncation in
[verifiers#2454](PrimeIntellect-ai/verifiers#2454)
(bash harness) and the copy bundled inside
[#147](#147)
(auto-compaction) — extracted standalone here so the cap can ship
independently; #147 can relocate/dedupe it when it lands.

Motivation from eval traces: unclipped tool results produced single-turn
context blowups (observed up to ~690k tokens from one `cat` on a large
file), which no compaction threshold can save you from after the fact.

Tests: truncation unit test added; 139 passed (6 pre-existing
`test_acp.py` env failures, same as clean main); ruff clean.
Comment thread verifiers/v1/harnesses/minimal/program.py Outdated
Comment thread verifiers/v1/harnesses/minimal/program.py
mikasenghaas and others added 7 commits August 28, 2026 22:02
Optional CompactionConfig on both in-house harnesses: compact into
a handoff summary at summarize_at_tokens, or at 90% of the model
context window when the provider advertises one. The threshold is
discovered by the agent loops themselves (the bash program reads
the provider's /models card; nano-rlm's engine already does), so
the interception server gains a stateless GET /v1/models relay
serving every dialect. On a provider overflow error the loops
compact and retry once, learning the threshold from the error
message; an oversized checkpoint request drops the newest tool
results one at a time until it fits.

RLM's summarize_at_tokens moves into the compaction config and
crosses ACP flat; nano-rlm pinned at f5c14aa.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Compact when 16k tokens remain below the context window instead of
at 90% of it - a fixed reserve keeps constant headroom on any
window size (small windows keep at least half). Truncate a tool
result over 10KB middle-out before it enters the conversation,
with a warning naming the original token count and line count, so
one giant output can never leap past the reserve and the model
knows what was cut. Matches Codex's output policy; the threshold
matches pi's reserve design. Pin nano-rlm 4fd3fa2 with the same
changes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Each marker now names the API whose error wording it matches, and
the unattributable generics are gone - "too many tokens" also
matches Bedrock throttling, and bare "context length"/"context
window" substrings matched more than they targeted. Pin nano-rlm
3b97900 with the same map.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The 413 markers could never fire: a 413 arrives as a plain
APIStatusError, not BadRequestError. Catch APIStatusError at the
compaction sites and gate overflow detection on a deterministic
status (400 or 413) so marker-shaped text in a transient failure
never triggers a compaction. Pin nano-rlm 4bb5f48 with the same
fix.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Final compaction design: proactive at a fixed reserve below a known
context window, reactive on attributed 400/413 overflow errors. A
rejected checkpoint no longer sheds tool results - it falls back to
the last state that passed a threshold check, which by definition
holds a full reserve of room; an empty or tool-calling reply is
resampled, and after three failed attempts the program ends the run
cleanly as a trainable sample instead of crashing. An overflow with
no history beyond the task propagates. Tool truncation grows to
20KB and the threshold-learning regexes go away - compaction now
requires a known window. Pin nano-rlm b928097 with the same design.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The RLM harness wiring moves to a stacked PR so this one can merge
before the nano-rlm companion lands. Also discover the context
window via models.list - the raw cast_to parse breaks on one Python
version or another (a bare dict cannot be constructed on 3.13, and
a parameterized dict trips inspect.isclass on 3.10).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A reasoning-parsed model (observed: Laguna via the glm45 parser) can
put the entire checkpoint reply in reasoning_content, leaving
content empty - every attempt then fails and the run ends as
compaction-failed despite a perfectly good summary. The checkpoint
asked for a summary, so when content is empty accept the reasoning
text as the summary.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment thread verifiers/v1/harnesses/minimal/program.py Outdated
vLLM 0.26 names the field "reasoning" and the SDK only keeps it in
model_extra, so the attribute lookup never saw it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment thread verifiers/v1/harnesses/minimal/program.py
A checkpoint reply that lives entirely in the reasoning channel is
resampled like an empty one - reasoning never enters the summary.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Review: last_good started at zero, so a first-turn checkpoint
rejection retried over an empty base - a summary of nothing with
the task gone; the initial conversation is now the floor. And a
multimodal MCP result is a content-part list, which the byte
truncation crashed on - only plain text is truncated.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment thread verifiers/v1/harnesses/minimal/program.py
Comment thread verifiers/v1/harnesses/bash/program.py Outdated
Review: interception hook rewrites entered the conversation
unbounded, sidestepping the 20KB tool-output limit. Every message
entering as a tool result now passes the same bound, rewrites
included.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment thread verifiers/v1/harnesses/minimal/program.py
Review: an overflow on the post-compaction work call propagated out
of the loop and crashed the rollout. The rebuilt conversation is
sized to fit by construction, so if it still overflows there are no
moves left - convert it to the compaction-failed clean ending.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment thread verifiers/v1/harnesses/minimal/program.py
Review: the post-tool snapshot was taken on a chars/4 estimate,
which can undercount dense content severalfold - the "good"
snapshot could itself be oversized, making the fallback identical
to the overflowing request. A state now becomes the fallback only
when the provider accepted that exact prompt with real usage below
the threshold, which lands the fallback before the tool results,
as designed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
# Conflicts:
#	verifiers/v1/harnesses/minimal/program.py

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want reviews to match your repository better? Bugbot Learning can learn team-specific rules from PR activity. A team admin can enable Learning in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 994a146. Configure here.

Comment thread verifiers/v1/harnesses/minimal/program.py
An overflow on a conversation that a compaction already reduced to
[system, summary] re-raised the provider error, so the rollout failed
instead of ending as a trainable sample like the in-cycle retry path.
Track that a compaction happened and convert that overflow to
CompactionFailed; the first-turn floor keeps raising.

Also give the /v1/models relay a finite read timeout so a hung
provider cannot stall threshold discovery for the rollout's whole
outer timeout.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
samsja
samsja previously approved these changes Aug 31, 2026

@hallerite hallerite left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mostly lgtm, small nit

Comment thread verifiers/v1/interception/server.py Outdated
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@mikasenghaas
mikasenghaas merged commit 6da786e into main Aug 31, 2026
13 checks passed
@mikasenghaas
mikasenghaas deleted the feat/context-compaction branch August 31, 2026 20:49
mikasenghaas added a commit that referenced this pull request Aug 31, 2026
## Summary

Follow-up to #2454: the building blocks that standalone chat programs
share were spread over `verifiers.v1.mcp.client`,
`harnesses/standalone.py`, and a monolithic `minimal/program.py`. This
PR gathers them in a new `verifiers.v1.harnesses.utils` package and
removes `harnesses.minimal` entirely:

- **`mcp.py`** — the bundleable MCP client helpers (`mcp_client`,
`connect_mcp`, `call_mcp`, `mcp_content_to_chat_content`, `with_retry`),
moved from `verifiers/v1/mcp/client.py`.
- **`compaction.py`** — tool-output bounding and context compaction
(markers, thresholds, `discover_threshold`, `truncate_tool_output`,
`Compactor`), extracted from `minimal/program.py`.
- **`core.py`** — the chat call, local tools (bash/edit/search), the
interception hook, the chat loop (`run_chat_loop`), plus argument
parsing, assembly, and the `__main__` entry point (inert on package
import; the entry point once the module ends the bundled script),
extracted from `minimal/program.py`.
- **`launch.py`** — `launch_chat_program`; `bundle_program(program,
*modules)`, which generalizes `inline_mcp_client` to splice any utils
modules into a PEP 723 program (moved from `standalone.py`); and
`CHAT_PROGRAM_SOURCE`, the shared Null/Bash program built as
`bundle_program(<PEP 723 metadata>, mcp, compaction, core)` — the
program text is only the metadata block.

The browser_use program keeps its own program file and bundles `mcp`
only. Cross-module references inside bundled code stay
`TYPE_CHECKING`-guarded, resolved by the flat bundle at runtime — same
pattern the programs already used.

Housekeeping picked up along the way:

- `verifiers.v1.mcp` keeps the host-side server infrastructure
(`Toolset`, `serve*`, `SharedToolServer`) and no longer re-exports the
client helpers — re-exporting from the new location would import the
whole `harnesses` package during `mcp`'s own import. The one runtime
consumer (`tasksets/nemo_gym/toolset.py`) imports from the new home.
- Drops a duplicated `CONTEXT_OVERFLOW_MARKERS` tuple that a merge left
in `minimal/program.py`.
- Narrows the `.gitignore` core-dump pattern from `core.*` to
`core.[0-9]*` (it swallowed `harnesses/utils/core.py`).
- `run_chat_loop` returning (instead of the loop's old early `return`
from `main()`) means the interception `tool_client` is now closed on the
null overflow path too.

No behavior changes otherwise: the bundled program is the same logic,
reordered.

## Verification

- `CHAT_PROGRAM_SOURCE` and the browser_use bundle compile.
- The bundled chat program runs in a fresh isolated uv env (`uv run
--no-project bundled.py --help` executes all spliced module-level code
against the declared script deps and reaches the `__main__` entry
point).
- `verifiers.v1` and `tasksets.nemo_gym.toolset` import cleanly; ruff +
unit tests pass.


<!-- Macroscope's pull request summary starts here -->
<!-- Macroscope will only edit the content between these invisible
markers, and the markers themselves will not be visible in the GitHub
rendered markdown. -->
<!-- If you delete either of the start / end markers from your PR's
description, Macroscope will append its summary at the bottom of the
description. -->
> [!NOTE]
> ### Consolidate shared harness code into `harnesses.utils` package
> - Moves MCP client, compaction utilities, and launch helpers into the
new `verifiers/v1/harnesses/utils` package, deleting the old
`harnesses/minimal` package
> - Introduces `bundle_program` in
[launch.py](https://github.com/PrimeIntellect-ai/verifiers/pull/2482/files#diff-f00f52fdf451c06e9bd80d3a7b1c0fe095bd3797ca76f6fec3a4cea166504016)
to replace `inline_mcp_client`; builds a shared `CHAT_PROGRAM_SOURCE`
constant used by the Bash, Null, and Browser-use harnesses
> - Extracts `run_chat_loop` from `main` in
[core.py](https://github.com/PrimeIntellect-ai/verifiers/pull/2482/files#diff-4641f7de541bf21c7f3e30a2d8d49f14c1179c70cf21f3dff5fa19deb9a1e206)
and moves all compaction logic into
[compaction.py](https://github.com/PrimeIntellect-ai/verifiers/pull/2482/files#diff-59ba1eb5314be61d1ec7c2c2f78ad8f0bbe70a855d9415f0b284f1630c2dc9dc)
> - Updates all harness imports to pull from the new `utils` modules
instead of `minimal` and `standalone`
> - Behavioral Change: `verifiers/v1/mcp.__init__` no longer re-exports
client helpers (`call_mcp`, `connect_mcp`, `mcp_client`,
`mcp_content_to_chat_content`, `with_retry`); any imports of those names
from `verifiers.v1.mcp` will fail at runtime — update imports to
`verifiers.v1.harnesses.utils.mcp`
>
> <!-- Macroscope's review summary starts here -->
>
> <sup><a href="https://app.macroscope.com">Macroscope</a> summarized
fa2e316.</sup>
> <!-- Macroscope's review summary ends here -->
>
<!-- Macroscope's pull request summary ends here -->

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
mikasenghaas added a commit to PrimeIntellect-ai/nano-rlm that referenced this pull request Sep 1, 2026
## Summary

- make context compaction an explicit `ExecutionPolicy.compaction`
option
- use an explicit threshold or discover one from the model card with 16k
tokens of headroom
- compact after threshold crossings, context-bound decodes, and
recognized provider overflows
- retry checkpoints from the latest usage-verified conversation when the
full checkpoint overflows
- resample empty or tool-calling checkpoints and end cleanly when
recovery is exhausted
- apply the same policy to root agents and recursive agents while
preserving IPython state
- truncate tool results over 20KB before they enter the conversation
- keep compaction prompts, context detection, and truncation helpers
outside the main loop

Companion: [verifiers
#2454](PrimeIntellect-ai/verifiers#2454).

## Breaking

- `ExecutionPolicy.compaction` now controls proactive and reactive
compaction.
- `ExecutionPolicy.summarize_at_tokens` now defaults to `None` instead
of `256_000`.
- Set `compaction=true` to enable compaction. Leave
`summarize_at_tokens` unset to use model-card discovery.

## Verification

- `uv run ruff check .`
- `uv run ruff format --check .`
- `UV_PROJECT_ENVIRONMENT=/tmp/nano-rlm-merge-tests.xApDnC/.venv uv sync
--group dev`
- `UV_PROJECT_ENVIRONMENT=/tmp/nano-rlm-merge-tests.xApDnC/.venv uv run
pytest tests/` — 157 passed

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Changes core LLM turn handling and default context behavior
(compaction off unless configured), so long runs and training
trajectories may diverge from prior releases; mistakes in overflow
detection or checkpoint retry could drop history or end runs early.
> 
> **Overview**
> **Context compaction is opt-in** instead of defaulting to a 256k token
threshold. Enable with `RLM_COMPACTION=1` or by setting
`RLM_SUMMARIZE_AT_TOKENS`; when compaction is on but no explicit
threshold is set, the engine discovers the model window via `/models`
and compacts at **window minus 16k** (small windows keep at least half).
Docs drop `RLM_MAX_OUTPUT` / `RLM_MAX_TOOL_OUTPUT_CHARS` in favor of a
fixed **20KB** head/tail truncation on tool results before they hit the
chat.
> 
> Compaction logic moves into **`rlm.compaction`** (overflow heuristics,
checkpoint prompts, truncation). The agent loop gains **`_complete`**:
proactive compaction after large tool turns, reactive compact-and-retry
on provider **400/413 context errors**, and on
**`finish_reason=length`** when usage crosses the threshold. Checkpoints
retry up to three times with **`_last_good` fallbacks**, plain-text-only
summaries (no tool calls / no reasoning channel), and
**`CompactionFailed`** clean stops instead of treating every oversized
body as fatal.
> 
> **`ExecutionPolicy.compaction`** and ACP/runtime snapshots expose the
flag; semantic edge tracking can **release and reclaim** summary
requests on resample. Tests cover overflow recovery, disabled
compaction, discovered thresholds, and sub-agents.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
f300615. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
mikasenghaas added a commit that referenced this pull request Sep 1, 2026
## Summary

- add the same optional `CompactionConfig` to the RLM harness as the
bash harness carries
- cross ACP with a flat policy: a `compaction` toggle plus
`summarize_at_tokens`
- pin merged nano-rlm compaction commit `4ef3438`

Builds on merged
[#2454](#2454), which
adds Bash compaction and the interception `/v1/models` relay.
Companion [nano-rlm
#147](PrimeIntellect-ai/nano-rlm#147) is merged.

## Breaking

- `RLMHarnessConfig.summarize_at_tokens` moves to
`RLMHarnessConfig.compaction.summarize_at_tokens` and no longer accepts
a `(lo, hi)` range.
- Leave `compaction` unset to disable proactive and reactive compaction.

## Verification

- `uv run pytest -q tests/v1` — passed; live E2E tests skipped without
`PRIME_API_KEY`
- `uv run pytest -q tests/v1/test_configs.py` — 12 passed
- `uv run ruff check verifiers/v1/harnesses/rlm/harness.py`
- `uv run ruff format --check verifiers/v1/harnesses/rlm/harness.py`

Terminal-Bench 2 e2e: 8 tasks, local vLLM `poolside/Laguna-XS-2.1` at
32k (glm45 reasoning + glm47 tool parsers), `compaction = {}` so the
engine discovers the threshold itself (`32768 − 16384 = 16384`). Trace
analysis of the pinned engine:

- Threshold discovery and the proactive trigger work through ACP:
compaction fired on the 3 episodes whose context crossed ~17k; episodes
that stayed below (0.7k-6.7k peaks) never compacted; 20KB tool
truncation visible where tool output was large.
- The runs surfaced and the pin fixes three integration bugs, each
verified against the failing trace: `/models` discovery crashing on
Python 3.10 containers (raw `cast_to` parse; now `models.list()`), and
two interactions with semantic-edge bookkeeping. A failed checkpoint
attempt and a resampled unusable reply each left the compaction's
summary-request claim held, which killed the retry with "compaction
already has a summary request".
- Laguna answers checkpoint prompts entirely in the reasoning channel,
so under the summaries-are-content-only rule its compactions exercise
the resample-then-end-cleanly path; summary carry-over across branches
was demonstrated on content-channel models (Qwen3-0.6B,
deepseek-v4-flash).
- A final combined verification run on a content-channel model is
pending before merge.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Breaking harness configuration and ACP runtime policy shape affect
rollout behavior and compaction timing; changes are localized to the RLM
harness but alter long-running agent context management.
> 
> **Overview**
> **RLM harness compaction** is restructured to match the bash harness:
a nested `CompactionConfig` with optional `summarize_at_tokens`, exposed
over ACP as `policy.compaction` (on/off) plus the threshold when set.
> 
> **Breaking config change:** `RLMHarnessConfig.summarize_at_tokens` is
removed in favor of `compaction`; the `(lo, hi)` per-task random range
and `summarize_threshold()` are dropped. **`compaction` unset** means
compaction is off; an **empty** `compaction` object enables automatic
thresholding (e.g. context window minus 16k when advertised).
> 
> The pinned **nano-rlm** ref updates to **`4ef3438`** for the merged
compaction engine. `_runtime_metadata` no longer takes `TaskData` since
thresholds are no longer task-index–seeded.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
5f39bb2. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

<!-- Macroscope's pull request summary starts here -->
<!-- Macroscope will only edit the content between these invisible
markers, and the markers themselves will not be visible in the GitHub
rendered markdown. -->
<!-- If you delete either of the start / end markers from your PR's
description, Macroscope will append its summary at the bottom of the
description. -->
> [!NOTE]
> ### Add `CompactionConfig` to `RLMHarness` and remove per-task
threshold randomization
> - Introduces `CompactionConfig` (derived from `BaseConfig`) with an
optional `summarize_at_tokens: PositiveInt` field, replacing the flat
`summarize_at_tokens` tuple-range field on `RLMHarnessConfig`.
> - Removes `RLMHarness.summarize_threshold`, which previously computed
per-task randomized thresholds seeded by task index. `_runtime_metadata`
now reads thresholds directly from `self.config.compaction` and emits a
boolean `compaction` flag in the session policy.
> - Changes the default `version` git ref for `RLMHarnessConfig` to
`4ef3438`.
> - Behavioral Change: `summarize_at_tokens` no longer accepts `(lo,
hi)` tuple ranges; callers must provide a single `PositiveInt` inside
`CompactionConfig`. The `data` parameter was removed from
`RLMHarness._runtime_metadata`.
>
> <!-- Macroscope's review summary starts here -->
>
> <sup><a href="https://app.macroscope.com">Macroscope</a> summarized
5f39bb2.</sup>
> <!-- Macroscope's review summary ends here -->
>
<!-- Macroscope's pull request summary ends here -->
mikasenghaas added a commit to PrimeIntellect-ai/prime-rl that referenced this pull request Sep 1, 2026
## Summary

- advance the `deps/verifiers` submodule from `e2103d6` to `d4a2177`
- include merged Bash and RLM context compaction from verifiers #2454
and #2459
- pick up nano-rlm compaction commit `4ef3438` through the default RLM
harness pin
- refresh `uv.lock` for verifiers' `aiohttp>=3.14.1` requirement
- migrate three RLM examples to `compaction.summarize_at_tokens`; use
the former range's `98_304` midpoint
- include the optional ACP semantic-edge and shared harness utility
changes already on verifiers `main`

Companions: [verifiers
#2454](PrimeIntellect-ai/verifiers#2454),
[verifiers
#2459](PrimeIntellect-ai/verifiers#2459), and
[nano-rlm #147](PrimeIntellect-ai/nano-rlm#147).

## Breaking

- RLM harness configs must move `summarize_at_tokens` to
`compaction.summarize_at_tokens`.
- Leave `compaction` unset to disable proactive and reactive compaction.

## Verification

- `git diff --check`
- `uv lock`
- `uv lock --check`
- `uv run pytest -q tests/unit/test_configs.py` — 133 passed
- `git submodule status deps/verifiers` —
`d4a217794fc0bfd70369a8230e56653c193da8ce`
- verified that `d4a2177` descends from the previous `e2103d6` pin

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Submodule bump plus a breaking harness config shape affects all RLM
runs still using the old `summarize_at_tokens` field; example migrations
are mechanical but custom configs must be updated.
> 
> **Overview**
> Advances the **`deps/verifiers`** submodule to pick up merged Bash/RLM
**context compaction** (and related harness defaults), with
**`uv.lock`** refreshed so **`aiohttp`** meets the new **`>=3.14.1`**
floor.
> 
> **Breaking for RLM harness TOML:** flat
**`env.agent.harness.summarize_at_tokens`** is replaced by
**`env.agent.harness.compaction.summarize_at_tokens`**. The three
advanced examples (**`glm-4.5-air/search`**, **`glm-4.5-air/terminal`**,
**`nemotron-3-super/swe`**) are updated accordingly—train sources that
used a two-threshold list **`[65536, 131072]`** now use a single
**`98304`** threshold; eval sources keep **`98304`** but under the
nested **`compaction`** key.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
7429aba. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
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.

3 participants