Skip to content

feat(compaction): optional compaction.model override + re-enable senpi compaction on SDK-native claude-sdk-oauth lanes - #737

Open
HeiTuz wants to merge 1 commit into
code-yeongyu:mainfrom
HeiTuz:compaction-model-override
Open

feat(compaction): optional compaction.model override + re-enable senpi compaction on SDK-native claude-sdk-oauth lanes#737
HeiTuz wants to merge 1 commit into
code-yeongyu:mainfrom
HeiTuz:compaction-model-override

Conversation

@HeiTuz

@HeiTuz HeiTuz commented Aug 6, 2026

Copy link
Copy Markdown

Problem

The claude-sdk-oauth lane keeps a resident Claude Agent SDK session, and the compaction lane policy (lane-policy.ts) stands down senpi compaction entirely on the assumption that the SDK runs its own native compaction. In practice that native compaction never fires for these sessions — senpi's session.log shows threshold/pre_prompt compactions repeatedly rejected with the Claude Agent SDK owns compaction for this session while context grows unbounded (observed past 335k tokens) until overflow, with no recovery path.

Change

Add an optional compaction.model setting ("provider/model", e.g. deepseek/deepseek-chat) that redirects only the compaction summarization call to a different model. When set:

  1. Lane policy (lane-policy.ts): the SDK-native stand-down lifts, so senpi auto/threshold compaction runs on the lane again. This is the escape hatch for lanes whose SDK never compacts.
  2. Execution (agent-session.ts_executeCompaction): a new _resolveCompactionModel() resolves the override through the model runtime and uses it for the summarization auth (_getCompactionRequestAuth) + compact() call. Session bookkeeping (lifecycle record, preparation, branch) still tracks the session model, so history/diagnostics are unaffected.
  3. Safe fallback: any resolution failure (unset / malformed provider/model / unknown model) falls back to the session model, so compaction can never silently break.

Default behavior is unchanged for every provider — with no override, nothing changes.

Surface

  • CompactionSettings.model?: string (settings-manager.ts, compaction/compaction.ts), exposed through getCompactionSettings().
  • LaneContext gains an optional getCompactionSettings getter so the lane policy can read the override.

Validation

  • New tests: lane-policy stand-down lift with override + continued stand-down when the override resolves to no model; settings model passthrough.
  • test/compaction/ suite: 47 files / 339 tests pass.
  • Root npm run check (biome + pinned-deps + ts-imports + shrinkwrap + tsc --noEmit): clean.

Summary by cubic

Adds an optional compaction.model override that redirects only the compaction summarization call to a specified model and re-enables senpi compaction on claude-sdk-oauth lanes when set. Prevents unbounded context growth when SDK-native compaction never fires, with no change to default behavior when unset.

  • New Features
    • compaction.model accepts "provider/model" (e.g., deepseek/deepseek-chat); used only for summarization and falls back to the session model if invalid or unresolved.
    • Lane policy: when an override is set on claude-sdk-oauth lanes, senpi auto/threshold compaction runs again instead of standing down.
    • Setting is exposed via getCompactionSettings() and resolved at execution time without affecting session bookkeeping or diagnostics.

Written for commit 95f7dfb. Summary will update on new commits.

Review in cubic

…i compaction on SDK-native claude-sdk-oauth lanes

The claude-sdk-oauth lane keeps a resident Claude Agent SDK session, and the
lane policy stands down senpi compaction entirely because the SDK is supposed
to run its own native compaction. In practice that native compaction never
fires, so these sessions grow unbounded until the context overflows with no
recovery path.

Add a "compaction.model" setting ("provider/model") that redirects only the
compaction summarization call to a different model. When it is set:

- the lane policy's SDK-native stand-down lifts, so senpi auto/threshold
  compaction runs on the lane again;
- _executeCompaction resolves the override model (via the model runtime) and
  uses it for the summarization auth + compact() call, while session
  bookkeeping (lifecycle record, preparation, branch) still tracks the session
  model;
- any resolution failure (unset / malformed / unknown model) falls back to the
  session model so compaction never silently breaks.

This gives the claude-sdk-oauth lane an escape hatch (e.g. compact on
deepseek/deepseek-chat) without changing default behavior for any provider.

Tests: lane-policy stand-down lift + no-override stand-down, settings
passthrough. Full compaction suite green (339 tests), typecheck clean.
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@1vivy

1vivy commented Aug 10, 2026

Copy link
Copy Markdown

Another production datapoint for the "native compaction never fires" claim, from a lane that ran to the window edge rather than to 335k.

Session: claude-sdk-oauth / claude-opus-5 (contextWindow: 1000000 per pi-ai anthropic.json), resumeMode: "auto", senpi 2026.8.9-2, compaction.enabled: true.

Prompt tokens (input + cacheRead + cacheWrite) per assistant turn, from the session JSONL:

03:37   81,308      06:04  659,972
04:07  212,414      07:13  807,897
04:37  371,067      19:12  938,654
05:19  530,435      19:50  983,261  <- max
                    20:20   75,098  <- after model_change off the lane

983,261 / 1,000,000 = 98.3% of the context window. senpi's adaptive threshold for a 1M window is 0.65 (policy.js: computeAdaptiveThresholdRatio), so compaction was due at ~650k, around 06:00 — it ran another ~14h past that, 290 billed assistant turns, $229.48, stopping ~17k tokens short of hard overflow. Context only dropped when the model was switched off the lane.

Two details that may be worth folding into the fix:

  1. Nothing is logged. logs/compaction.log has zero lines for this session id and zero lane-rejection lines. The message_end monitor hook is itself gated (compaction/index.js:650, isMonitorableMessageEvent(event) && !lanePolicy.disablesSenpiCompaction(ctx)), so usage is never tracked on the lane and there is no rejection to log. The failure is silent, not just unrecovered — which makes it hard for users to notice before the bill.

  2. Zero boundaries were ever mirrored. No claude-sdk-oauth-compact entries in the session, so the SDK emitted no compact_boundary at any point up to 98.3%. That is the production observation feat(claude-sdk-oauth): compaction alignment, restart continuity, and the permanent gate #637's gate matrix explicitly could not produce (phase (f) was skipped because "the loopback can't make the real CLI emit a compact_boundary").

One question on the shape of this PR: since the stand-down only lifts when compaction.model is set, a default claude-sdk-oauth + resumeMode: auto session still never compacts after this merges. Is a default-side backstop in scope here (e.g. re-arming the usage monitor on the lane, or lifting the stand-down once usage crosses the threshold with no boundary observed), or is that intended as a separate change?

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.

2 participants