Skip to content

fix: add session-growth guard to prevent unbounded session store growth#11999

Closed
reverendrewind wants to merge 3 commits into
openclaw:mainfrom
reverendrewind:fix/session-growth-guard-11971
Closed

fix: add session-growth guard to prevent unbounded session store growth#11999
reverendrewind wants to merge 3 commits into
openclaw:mainfrom
reverendrewind:fix/session-growth-guard-11971

Conversation

@reverendrewind

@reverendrewind reverendrewind commented Feb 8, 2026

Copy link
Copy Markdown

Summary

Fixes #11971.

  • Context pruning (cache-ttl) and DM history limiting modify messages in-flight before the API call but never touch the session store. This masks the true session size from the pi-coding-agent library's auto-compaction trigger, causing the JSONL store to grow unbounded.
  • Non-Anthropic providers sharing the same session then receive the full unpruned history (854K+ tokens observed in production, costing $0.66 in wasted API calls).
  • Adds a provider-agnostic pre-prompt guard in runEmbeddedPiAgent that estimates actual stored tokens via SessionManager.open() and forces compaction when the store exceeds 80% of the context window.

Changes

File Change
src/agents/pi-embedded-runner/compact.ts Add estimateSessionFileTokens() helper
src/agents/pi-embedded-runner/run.ts Add SESSION_GROWTH_COMPACTION_THRESHOLD and pre-prompt guard with try/catch
src/agents/pi-embedded-runner/run.session-growth-guard.test.ts 6 new tests

Design decisions

  • Provider-agnostic: fires regardless of current provider, catching both cache-ttl pruning (Anthropic) and limitHistoryTurns (DM sessions)
  • Best-effort: errors in session estimation are caught and logged, never block the prompt
  • Runs once before the retry loop (existing overflow handling covers subsequent growth)
  • Threshold at 80%: above pruning ratios (30-50%) to avoid interference, but catches clearly bloated sessions

Test plan

  • 6 new tests pass (threshold logic, compaction success/failure, error resilience, no-session-file, compaction counting)
  • 8 existing overflow-compaction tests pass (no regressions)
  • Linter passes (0 warnings, 0 errors)

🤖 Generated with Claude Code

Greptile Overview

Greptile Summary

Adds a pre-prompt “session-growth guard” to runEmbeddedPiAgent that estimates the stored token count from the session JSONL and forces compaction when it exceeds 80% of the model context window. This is intended to prevent unbounded session store growth when downstream pruning/history-limiting modifies messages only in-flight.

Also introduces estimateSessionFileTokens() to compute an approximate token count from the on-disk session context, and adds a dedicated test suite for the guard behavior.

Confidence Score: 4/5

  • This PR looks safe to merge after fixing a test that relies on an invalid type cast.
  • The runtime change is isolated (best-effort guard + existing compaction path) and covered by new tests, but one test currently bypasses the real params contract by casting undefined to a required string, which can mask mismatches between intended and actual runtime states.
  • src/agents/pi-embedded-runner/run.session-growth-guard.test.ts

(2/5) Greptile learns from your feedback when you react with thumbs up/down!

Context used:

  • Context from dashboard - CLAUDE.md (source)
  • Context from dashboard - AGENTS.md (source)

…th (#11971)

Context pruning (cache-ttl) and DM history limiting modify messages
in-flight before the API call but never touch the session store.  This
masks the true session size from the pi-coding-agent library's
auto-compaction trigger, causing the JSONL store to grow unbounded.
Non-Anthropic providers then receive the full unpruned history (854K+
tokens observed in production).

Add a pre-prompt guard in runEmbeddedPiAgent that estimates actual
stored tokens via SessionManager and forces compaction when the store
exceeds 80% of the context window.  The guard is provider-agnostic,
best-effort (errors are logged, never block the prompt), and runs once
before the retry loop.

- Add estimateSessionFileTokens() to compact.ts
- Add SESSION_GROWTH_COMPACTION_THRESHOLD (0.8) and guard in run.ts
- Add 6 tests in run.session-growth-guard.test.ts
- Existing overflow-compaction tests unaffected (8/8 pass)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@openclaw-barnacle openclaw-barnacle Bot added the agents Agent runtime and tooling label Feb 8, 2026

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

1 file reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Comment on lines +269 to +272
it("skips guard when no session file is provided", async () => {
const paramsNoFile = { ...baseParams, sessionFile: undefined as unknown as string };

mockedRunEmbeddedAttempt.mockResolvedValueOnce(makeAttemptResult());

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Invalid param type cast

RunEmbeddedPiAgentParams.sessionFile is a required string (src/agents/pi-embedded-runner/run/params.ts:57), but this test forces sessionFile to undefined via undefined as unknown as string. That defeats type-safety and makes the test cover a state that can’t occur for real callers, while also hiding that if (params.sessionFile) in run.ts is effectively dead under the current type.

Consider either (a) making sessionFile optional in the real params type (if “no session file” is a valid runtime scenario), or (b) update the test to pass a real string and instead cover the “missing file on disk” path (which estimateSessionFileTokens already handles) without type casting.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agents/pi-embedded-runner/run.session-growth-guard.test.ts
Line: 269:272

Comment:
**Invalid param type cast**

`RunEmbeddedPiAgentParams.sessionFile` is a required `string` (src/agents/pi-embedded-runner/run/params.ts:57), but this test forces `sessionFile` to `undefined` via `undefined as unknown as string`. That defeats type-safety and makes the test cover a state that can’t occur for real callers, while also hiding that `if (params.sessionFile)` in `run.ts` is effectively dead under the current type.

Consider either (a) making `sessionFile` optional in the real params type (if “no session file” is a valid runtime scenario), or (b) update the test to pass a real string and instead cover the “missing file on disk” path (which `estimateSessionFileTokens` already handles) without type casting.

How can I resolve this? If you propose a fix, please make it concise.

rimskij and others added 2 commits February 9, 2026 07:49
Address Greptile review: remove `undefined as unknown as string` cast
that defeats type-safety. Replace with empty-string test (valid falsy
string) and add nonexistent-file test covering the real runtime path.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@openclaw-barnacle

Copy link
Copy Markdown

This pull request has been automatically marked as stale due to inactivity.
Please add updates or it will be closed.

@openclaw-barnacle openclaw-barnacle Bot added stale Marked as stale due to inactivity and removed stale Marked as stale due to inactivity labels Feb 21, 2026
@steipete

Copy link
Copy Markdown
Contributor

Closing as AI-assisted stale-fix triage.

Linked issue #11971 ("Bug: contextPruning (cache-ttl) masks session size from compaction, causing unbounded growth for non-Anthropic models") is currently CLOSED and was closed on 2026-02-23T04:33:33Z with state reason NOT_PLANNED.
Given that issue state, this fix PR is no longer needed in the active queue and is being closed as stale.

If the underlying bug is still reproducible on current main, please reopen this PR (or open a new focused fix PR) and reference both #11971 and #11999 for fast re-triage.

@steipete

Copy link
Copy Markdown
Contributor

Closed after AI-assisted stale-fix triage (closed issue duplicate/stale fix).

@steipete steipete closed this Feb 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: contextPruning (cache-ttl) masks session size from compaction, causing unbounded growth for non-Anthropic models

2 participants