diff --git a/docs-site/src/content/docs/guides/model-routing.md b/docs-site/src/content/docs/guides/model-routing.md index b9f1a6b34da..ac8e6ae62bb 100644 --- a/docs-site/src/content/docs/guides/model-routing.md +++ b/docs-site/src/content/docs/guides/model-routing.md @@ -91,6 +91,10 @@ Routing and catalog visibility are separate controls: deliberately inert for them. - `provider.disabled: true` removes that provider from catalog discovery. Explicit `provider/model` requests fail, and `defaultModel` / `models[]` scans skip it. +- Routed catalog rows are cloned from the Codex-native template, so native-only delivery flags are + stripped during normalization: `supports_websockets`, `supports_reasoning_summaries`, and + `supports_experimental_context`. A routed provider never inherits the experimental-context + contract; inheriting it made Codex compact after nearly every step on long routed threads. - `providerContextCaps` applies per-provider Codex-visible context caps. `contextCapValue` is the dashboard default (350,000 by default), but it does nothing by itself until a provider is present in `providerContextCaps`. Changing the dashboard value updates every enabled cap diff --git a/src/codex/catalog/parsing.ts b/src/codex/catalog/parsing.ts index fd9a4db1784..d802284aeef 100644 --- a/src/codex/catalog/parsing.ts +++ b/src/codex/catalog/parsing.ts @@ -803,6 +803,13 @@ export function normalizeRoutedCatalogEntry( delete entry.multi_agent_reasoning_effort; delete entry.use_responses_lite; delete entry.supports_websockets; + // Routed rows cloned from native templates must not inherit OpenAI-only experimental context + // delivery. Codex reads the flag as "this model accepts experimental context history" and drives + // its context-management cadence from it, so a third-party provider that never negotiated it gets + // a compact-after-every-step loop instead (observed on a routed DeepSeek row: 1,600+ compactions + // in a single thread). Nothing re-applies the field from provider metadata during this step, so + // the row leaves this normalization without it. + delete entry.supports_experimental_context; /* * Tier metadata is stripped from routed rows because a row cloned from a native template * would otherwise hand a third-party provider OpenAI's tiers. diff --git a/tests/e2e-style/phase100-native-parity.test.ts b/tests/e2e-style/phase100-native-parity.test.ts index 04088ace30d..abe4f8e51b2 100644 --- a/tests/e2e-style/phase100-native-parity.test.ts +++ b/tests/e2e-style/phase100-native-parity.test.ts @@ -18,6 +18,7 @@ function nativeTemplate(): Record { multi_agent_version: "v2", use_responses_lite: true, supports_websockets: true, + supports_experimental_context: true, web_search_tool_type: "text_and_image", supports_search_tool: true, supported_reasoning_levels: [ @@ -88,6 +89,7 @@ describe("Phase 100 Codex-native parity smoke", () => { expect(routed).not.toHaveProperty("model_messages"); expect(routed).not.toHaveProperty("use_responses_lite"); expect(routed).not.toHaveProperty("supports_websockets"); + expect(routed).not.toHaveProperty("supports_experimental_context"); const parsed = parseRequest({ model: "opencode-go/deepseek-v4-flash",