Skip to content

fix(claude-provider): use [1m] model tag for reliable 1M context#2280

Closed
slambert wants to merge 1 commit into
nanocoai:mainfrom
slambert:fix/1m-context-model-tag
Closed

fix(claude-provider): use [1m] model tag for reliable 1M context#2280
slambert wants to merge 1 commit into
nanocoai:mainfrom
slambert:fix/1m-context-model-tag

Conversation

@slambert
Copy link
Copy Markdown

@slambert slambert commented May 5, 2026

Summary

  • Plumb the model field from container.json through the agent-runner config, provider options, and into the Claude SDK query() call
  • Use the CLI's [1m] model tag (e.g. claude-sonnet-4-6[1m]) instead of --betas to signal 1M context — the CLI strips the tag before API calls via its internal normalizer
  • Compute CLAUDE_CODE_AUTO_COMPACT_WINDOW from the model's actual context window (80% of 1M = 800K for Sonnet/Opus) instead of hardcoding 165K
  • Allow env var override for CLAUDE_CODE_AUTO_COMPACT_WINDOW when explicitly set

Why [1m] instead of --betas?

The CLI's context window function checks three paths in order:

  1. [1m] tag in model name — unconditional, no dependencies
  2. --betas flag — requires sdkBetas to be stored in global state first
  3. Remote config lookup — unavailable in headless containers

In headless containers, path 3 is unavailable and path 2 has a race condition: the betas store may not complete before the compaction window is computed. This causes 1M models to compact at ~168K tokens (80% of the 200K default) instead of ~800K.

The [1m] tag (path 1) is checked first via a simple regex (/\[1m\]/i.test(modelName)) with no async dependencies, making it reliable in all environments.

Related

Test plan

  • Set "model": "claude-sonnet-4-6" in a group's container.json
  • Start a session — verify CLAUDE_CODE_AUTO_COMPACT_WINDOW is 800000
  • Fill context past 168K tokens — verify compaction does NOT trigger until ~800K
  • Verify CLAUDE_CODE_AUTO_COMPACT_WINDOW env var override still works when explicitly set
  • Verify models not in the 1M set (e.g. Haiku) get 200K default and no [1m] tag

🤖 Generated with Claude Code

…eadless containers

The Claude Code CLI determines context window size internally via three
paths (checked in order):

1. `[1m]` tag in model name — unconditional, no dependencies
2. `--betas context-1m-2025-08-07` — requires CLI global state
3. Remote config (coral_reef_sonnet) — unavailable in headless containers

Path 2 has a race condition: the CLI stores betas via sdkBetas during
initialization, but the compaction window can be computed before that
store completes. When this happens, CLAUDE_CODE_AUTO_COMPACT_WINDOW
gets capped by Math.min(200000, 800000) = 200000, causing compaction
at ~160K tokens instead of ~800K.

This commit:
- Adds `model` field to ContainerConfig, RunnerConfig, and ProviderOptions
- Passes model through from container.json → runner → Claude provider
- Introduces `modelForSdk()` which appends `[1m]` to known 1M models
- Replaces the hardcoded compact window with `compactWindowForModel()`
  that derives the threshold from the model's context window (80%)
- Preserves the CLAUDE_CODE_AUTO_COMPACT_WINDOW env var as an operator
  override (checked first, before model-based computation)

The CLI's internal normalizer (Fj) strips the [1m] tag before API
calls, so the Anthropic API still receives the canonical model name.

To use: set `"model": "claude-sonnet-4-6"` in the group's
container.json (or base-container.json). The provider auto-appends
[1m] for 1M-capable models.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gavrielc
Copy link
Copy Markdown
Collaborator

gavrielc commented May 9, 2026

Thanks for the thorough analysis of the [1m] tag vs --betas race condition, that was a good catch.

With #2233 merged, per-group model overrides are now wired end-to-end. That said, we're going to pass on auto-expanding the compaction window based on context size. Shorter sessions work better for claw-type agents. We'd rather compact earlier and keep turns focused than let context grow to 800K. The current 165K default is intentional.

Closing this one out, but appreciate the investigation.

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