Copilot long_context window reverts on restart: startup OAuth reconciliation deletes providers.github-copilot.modelContextWindows #7092
Description
Client or integration
Codex App
Area
Catalog / models
Summary
The documentation tells users to opt a GitHub Copilot model into the upstream long-context tier with providers.github-copilot.modelContextTiers.<model> = "long_context" plus a per-model window in providers.github-copilot.modelContextWindows ("It raises the advertised window only when that exact model has a known per-model window in modelContextWindows (for example, 1000000)" — docs-site/.../guides/model-routing.md).
The tier survives restarts, but the per-model window does not. On every startup reconcileOAuthProviders() → projectOAuthProviderReconciliation() → applyOAuthPresetCatalog() runs for github-copilot (authMode: "oauth"), and because the registry preset defines no modelContextWindows and the field is not in OAUTH_PRESERVE_WHEN_PRESET_UNSET, the delete branch removes the operator's map from config.json.
Result: on every start the Codex catalog row for github-copilot/gpt-6.1-sol falls back to GitHub Copilot's live capabilities.limits.max_context_window_tokens (400000), while requests still ride contextTier: "long_context" upstream. The only way to keep the advertised 1.05M window is to re-run ocx models set github-copilot/gpt-6.1-sol --context-window 1050000 after every start (I currently automate that with an external scheduled task).
The src/oauth/index.ts comment states this is deliberate ("The input side (contextWindow, modelContextWindows) describes what the account's models are, which the preset and live discovery do own"), but for github-copilot the preset owns nothing here and live discovery only ever reports the default-tier window, so the documented long-context workflow has no persistent path. modelContextTiers is preserved across restarts (and across OAuth upserts), which makes the asymmetry look unintended.
Expected: an explicit operator-set per-model window — at minimum the per-model evidence for a long_context tier the operator enabled — should survive startup reconciliation; otherwise the docs should say it does not and describe the supported path.
Reproduction
ocx provider edit github-copilot --model-context-tier gpt-6.1-sol=long_contextocx models set github-copilot/gpt-6.1-sol --context-window 1050000- Confirm the live state:
/api/modelsand the Codex catalog (~/.codex/models_cache.json,cc-switch-model-catalog.json) reportcontextWindow: 1050000forgithub-copilot/gpt-6.1-sol. - Restart OpenCodex (or re-run
ocx start). ~/.opencodex/config.jsonno longer containsproviders.github-copilot.modelContextWindows(onlymodelContextTiersremains), and the catalog row is back tocontextWindow: 400000.
Reproduced on 2.81.0. The same reconcile code is present at the v2.84.0 target commit (6955343008), so I expect it to still reproduce there.
Version
2.81.0
Operating system
Windows 11 24H2
Provider and model
github-copilot / gpt-6.1-sol (Copilot Business seat; live /models reports capabilities.limits.max_context_window_tokens: 400000)
Logs or error output
# after a restart, config diff:
- "modelContextWindows": { "gpt-6.1-sol": 1050000 } # removed by startup reconciliation
"modelContextTiers": { "gpt-6.1-sol": "long_context", "gpt-6-sol": "long_context" } # kept
Redacted configuration
{
"providers": {
"github-copilot": {
"adapter": "openai-chat",
"baseUrl": "https://api.githubcopilot.com",
"authMode": "oauth",
"liveModels": true,
"modelContextTiers": {
"gpt-6.1-sol": "long_context",
"gpt-6-sol": "long_context"
},
"modelContextWindows": {
"gpt-6.1-sol": 1050000
}
}
}
}(before restart — modelContextWindows is removed by the startup reconcile)
Checks
- I searched existing issues and documentation.
- I removed secrets, tokens, account details, request credentials, and personal data.
Activity
geekxm commentedon Oct 14, 2026
Confirming this reproduces on a different OAuth provider and a different OS, and adding
one detail I think changes the severity: on this provider the operator's map is not merely
removed — it is replaced by stale registry seeds that silently cap the advertised window.
Environment
@bitkyc08/opencodex2.84.0, macOS 27.0.1 (build 26A434), Apple Silicon arm64xai(authMode: "oauth", OAuth/Grok-subscription lane) — notgithub-copilotxai/grok-composer-2.5-fast(in the registry rosterXAI_MODELS, but with no livecontext metadata, so it has no seed and no discovery value)
Exact code path (matches your diagnosis)
src/oauth/index.ts:The
xaipreset definesmodels: XAI_MODELSbut nomodelContextWindows(
src/providers/registry/entries-core.ts:320), so the delete branch fires.The asymmetry you observed is therefore structural, not Copilot-specific:
modelMaxOutputTokensis inOAUTH_PRESERVE_WHEN_PRESET_UNSETand survives, whilemodelContextWindowsis reconciled and has no preset value. I see exactly that split onxai:after repeated restarts
modelMaxOutputTokensstill held 17 operator entries whilemodelContextWindowsheld only the seed set.What makes it worse than "the field is removed"
The field is not left absent —
enrichProviderFromRegistryre-seeds it (fill-only, as thecomment at lines 1233-1237 describes). So the operator's map is overwritten by seed values,
and some of those seeds are larger than what the provider actually serves:
xai.modelContextWindows/v1/modelsforgrok-composer-2.5-fastocx config set providers.xai.modelContextWindows '{...}'grok-composer-2.5-fastkeys present at200000context_window: 200000200000(router kept the loaded value)context_windowabsentBecause
modelContextWindowscaps rather than raises, the stale seed is usually harmless —but it is still wrong state the operator cannot correct durably.
xai/grok-4.5is seeded as500000while live discovery reports256000, so the saved row disagrees with what is served,and only the cap keeps the advertised value correct. On a model with neither a seed nor live
metadata (
grok-composer-2.5-fast) the field is simply gone, and every client sees nocontext_windowat all.Reproduction on macOS (deterministic)
xaion the OAuth lane.ocx config set providers.xai.modelContextWindows '{"grok-composer-2.5-fast":200000,"grok-composer-2.5-fast--fast":200000, ...}'(also reproducible via
POST /api/providers {name:"xai", provider:{...}})~/Library/Application Support/.../config.json(and~/.opencodex/config.json) now showsthe added keys;
GET /v1/modelsreportscontext_window: 200000.config.json;GET /v1/modelsstill reports200000for that session.GET /v1/modelsno longer reportscontext_windowfor that model.This reproduces even when the proxy is stopped before the edit, so it is the startup
reconcile rather than the shutdown flush in #488.
Suggested direction
The file already contains the fix pattern for this exact hazard — lines 1233-1237 explain that
requiresReasoningPlaceholderModelsis deliberately kept out ofOAUTH_RECONCILE_FIELDSbecause "the delete-when-preset-undefined branch would wipe an explicit user opt-out (
[]) onevery startup".
modelContextWindowshas the same property today: no OAuth preset seeds it, yet operators areexpected to set it (the long-context workflow in your issue, and per-model fallbacks for models
whose provider publishes no window). Either excluding it from
OAUTH_RECONCILE_FIELDS, oradding it to
OAUTH_PRESERVE_WHEN_PRESET_UNSET, would make it behave likemodelMaxOutputTokensalready does.Workaround in use
A small script re-applies the operator's
modelContextWindowsentries and re-verifies/v1/modelsafter each restart. Happy to test a patch if you want a second environment.