fix(providers): route GitHub Copilot Responses-only models off chat completions - #746
fix(providers): route GitHub Copilot Responses-only models off chat completions#746mushikingh wants to merge 2 commits into
Conversation
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughGitHub Copilot now maps mixed model catalogs to ChangesGitHub Copilot mixed-wire routing
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant Client
participant Router
participant AdapterResolver
participant CopilotResponses
participant CredentialRecovery
Client->>Router: select github-copilot/model
Router->>AdapterResolver: resolve model adapter
AdapterResolver->>CopilotResponses: route Responses-only model
CopilotResponses-->>CredentialRecovery: return 401 or 429
CredentialRecovery->>CopilotResponses: refresh or rotate and replay
CopilotResponses-->>Client: return response or final failure
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1🛠️ Fix failing CI checks 💡
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs-site/src/content/docs/guides/providers.md`:
- Line 93: Change the “GitHub Copilot model wires” heading from an h4 to an h3
so it is correctly nested beneath the surrounding “## 2. Account login (OAuth)”
section and satisfies the heading hierarchy.
In `@docs-site/src/content/docs/ja/guides/providers.md`:
- Around line 201-204: Update the model-routing descriptions in
docs-site/src/content/docs/ja/guides/providers.md lines 201-204 and
docs-site/src/content/docs/ko/guides/providers.md lines 201-204 to state that
gpt-5.4 remains routed to Responses by default, while text-only Chat Completions
are supported; retain the requirement for Responses when real Codex requests use
function tools and reasoning, without changing the handling of the other listed
models.
In `@docs-site/src/content/docs/ru/guides/providers.md`:
- Line 94: The Copilot provider guidance is incomplete in both translated pages.
Update docs-site/src/content/docs/ru/guides/providers.md lines 94-94 and
docs-site/src/content/docs/zh-cn/guides/providers.md lines 187-191 to include
the /chat/completions failure message, the gpt-5.4 text-only caveat, remaining
openai-chat behavior, wire-default details, and the manual selection command
codex -m github-copilot/...; keep each translation synchronized with the actual
CLI/API behavior.
In `@src/router.ts`:
- Around line 312-318: Update knownModelIdsForProvider, used by
providersServingModelId, to include the keys from prov.modelAdapters in the
returned model-ID set alongside existing sources. Add a router test covering a
provider configured only with modelAdapters, verifying its adapted model appears
among bare-model selector alternatives.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 6da0615d-686e-4480-a737-ce612a5682da
📒 Files selected for processing (14)
docs-site/src/content/docs/guides/providers.mddocs-site/src/content/docs/ja/guides/providers.mddocs-site/src/content/docs/ko/guides/providers.mddocs-site/src/content/docs/ko/reference/configuration.mddocs-site/src/content/docs/reference/configuration.mddocs-site/src/content/docs/ru/guides/providers.mddocs-site/src/content/docs/zh-cn/guides/providers.mdsrc/providers/model-wire-defaults.tssrc/providers/registry.tssrc/router.tssrc/server/adapter-resolve.tstests/adapter-resolve.test.tstests/github-copilot-wire-defaults.test.tstests/router.test.ts
| /** Enabled non-canonical providers whose known model ids include this bare OpenAI-family id. */ | ||
| function providersServingModelId(config: OcxConfig, modelId: string): string[] { | ||
| return activeProviderEntries(config) | ||
| .filter(([provName, prov]) => provName !== OPENAI_CODEX_PROVIDER_ID | ||
| && knownModelIdsForProvider(provName, prov).includes(modelId)) | ||
| .map(([provName]) => provName); | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Include configured modelAdapters keys in selector alternatives.
providersServingModelId relies on knownModelIdsForProvider, but that helper does not include Object.keys(prov.modelAdapters ?? {}). Therefore, a provider configured only with:
modelAdapters: { "gpt-5.5": "openai-responses" }can serve provider/gpt-5.5 while the bare-model error omits that provider from its suggested alternatives. Add modelAdapters keys to the known-ID set and cover this configuration in tests/router.test.ts.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/router.ts` around lines 312 - 318, Update knownModelIdsForProvider, used
by providersServingModelId, to include the keys from prov.modelAdapters in the
returned model-ID set alongside existing sources. Add a router test covering a
provider configured only with modelAdapters, verifying its adapted model appears
among bare-model selector alternatives.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ae0f9c41dc
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| "github-copilot": new Map( | ||
| [...GITHUB_COPILOT_RESPONSES_MODELS].map(id => [id, "openai-responses"] as const), | ||
| ), |
There was a problem hiding this comment.
Preserve compat sampling for Copilot Responses routes
When a Chat Completions or Anthropic Messages client selects one of these Copilot models, this default makes the preliminary resolver report openai-responses. Both compat handlers currently treat any openai-responses route as native ChatGPT and delete max_output_tokens plus sampling/stop fields before calling handleResponses (src/server/chat-completions.ts:91, src/server/claude-messages.ts:602). Copilot is a non-forward Responses gateway, and createResponsesPassthroughAdapter preserves those fields for key/OAuth mode, so max_tokens, stop, temperature, etc. silently stop working only for the newly defaulted models; gate the stripping on isCanonicalOpenAiForwardProvider or equivalent.
Useful? React with 👍 / 👎.
| "github-copilot": new Map( | ||
| [...GITHUB_COPILOT_RESPONSES_MODELS].map(id => [id, "openai-responses"] as const), | ||
| ), |
There was a problem hiding this comment.
Keep OAuth 401 refresh for Copilot Responses routes
For OAuth Copilot accounts whose access token is rejected before its recorded expiry, this mapping sends the request through the openai-responses passthrough branch. The existing forced-refresh-and-replay for github-copilot 401s is only in the normal adapter loop after the passthrough branch (src/server/responses/core.ts:2150), while passthrough non-2xx responses are formatted immediately, so these models now surface a 401 repeatedly instead of refreshing the Copilot token as the chat-wire Copilot models still do. Add the same 401 replay before returning passthrough errors or avoid the passthrough branch for OAuth Copilot.
AGENTS.md reference: src/AGENTS.md:L19-L20
Useful? React with 👍 / 👎.
| const MODEL_WIRE_DEFAULTS: Record<string, ReadonlyMap<string, string>> = { | ||
| "github-copilot": new Map( | ||
| [...GITHUB_COPILOT_RESPONSES_MODELS].map(id => [id, "openai-responses"] as const), | ||
| ), |
There was a problem hiding this comment.
Move Copilot wire defaults into the registry
This table hard-codes provider/model facts for github-copilot in a new picker that is separate from PROVIDER_REGISTRY, while the same commit also updates the Copilot registry seed and tests that the two lists stay aligned. That creates exactly the drift-prone split the repository tries to avoid; add the per-model wire-default metadata to the canonical provider registry/derivation path and have the resolver read it from there instead of maintaining a second provider fact table.
AGENTS.md reference: src/AGENTS.md:L18-L18
Useful? React with 👍 / 👎.
| "github-copilot": new Map( | ||
| [...GITHUB_COPILOT_RESPONSES_MODELS].map(id => [id, "openai-responses"] as const), | ||
| ), |
There was a problem hiding this comment.
Preserve key-pool 429 retry for Copilot Responses routes
For github-copilot users running key-auth override with an apiKeyPool, these defaults move the affected models from the normal adapter loop into the passthrough branch. The normal loop handles upstream 429s with hasKeyPoolFailover / rotateProviderTransportOn429, but passthrough non-OK responses are returned immediately (src/server/responses/core.ts:1580), so only the newly defaulted Copilot models stop rotating to a healthy key on quota/rate-limit responses. Add equivalent 429 rotation before formatting passthrough failures or keep these models on a non-passthrough path that preserves key failover.
Useful? React with 👍 / 👎.
Codex review — all four findings verified and addressed (
|
| Failing test | Branch | Base |
|---|---|---|
claude-desktop-cli — persisted profile |
fail | fail |
claude-desktop-cli — import rejects invalid |
fail | fail |
cli-help — service usage remove alias |
fail | fail (expectation predates repair) |
provider-workspace-auth — logout/delete states |
fail | fail (greps GUI for setToast(...), now showActionFeedback(...)) |
decodeSchtasksOutput — UTF-16LE BOM |
fail | fail |
cli-restore-back — ambiguous cleanup |
fail (5017ms timeout) | passes in isolation — 5s-timeout flake under load |
None are caused by this PR, and I did not touch GUI or Windows-scheduler code.
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/server/chat-completions.ts (1)
86-103: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDuplicated Responses-wire sampling-param stripping across two handlers — extract a shared helper.
src/server/chat-completions.tsandsrc/server/claude-messages.tsboth carry an identical block that conditionally stripsmax_output_tokens(forward-only) and unconditionally stripstemperature/top_p/stop/userwhenroute.provider.adapter === "openai-responses". This PR itself had to patch both copies in lockstep to fix the Copilot routed-gateway bug — proof that keeping this logic duplicated risks the two call sites silently diverging again the next time a Responses-wire nuance needs handling.
src/server/chat-completions.ts#L86-L103: extract thestore/max_output_tokens/temperature/top_p/stop/usershaping block (currently inline here) into a shared helper, e.g.applyResponsesWireParamShaping(internalBody, route.provider).src/server/claude-messages.ts#L600-L616: replace the identical inline block with a call to the same shared helper so both handlers stay in sync going forward.♻️ Suggested shared helper sketch
// e.g. src/server/responses/param-shaping.ts export function stripUnsupportedResponsesSamplingParams( body: Rec, provider: Pick<OcxProviderConfig, "authMode">, ): void { // Forward mode is the only route that 400s on max_output_tokens; a routed Responses // gateway (Copilot, api.openai.com, ...) honors the field. if (provider.authMode === "forward") delete body.max_output_tokens; // temperature/top_p/stop/user stay stripped on every Responses route. delete body.temperature; delete body.top_p; delete body.stop; delete body.user; }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/server/chat-completions.ts` around lines 86 - 103, Extract the Responses-wire parameter shaping from the inline block near the route handling in src/server/chat-completions.ts:86-103 into a shared helper that applies the forward-only max_output_tokens removal and unconditional temperature, top_p, stop, and user removal, while preserving store handling. Replace the duplicate block in src/server/claude-messages.ts:600-616 with the same helper call so both handlers use one implementation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/server/responses/core.ts`:
- Around line 1519-1576: Update the recovery replay loop around noteAttemptSend
in the routed Responses passthrough to track the recovery kind that triggered
each replay. Set the kind to "oauth-401" for the OAuth refresh branch and
"key-429" for the key-rotation branch, then pass it as the recovery argument to
noteAttemptSend so recoveryKinds reflects the corresponding send.
---
Outside diff comments:
In `@src/server/chat-completions.ts`:
- Around line 86-103: Extract the Responses-wire parameter shaping from the
inline block near the route handling in src/server/chat-completions.ts:86-103
into a shared helper that applies the forward-only max_output_tokens removal and
unconditional temperature, top_p, stop, and user removal, while preserving store
handling. Replace the duplicate block in src/server/claude-messages.ts:600-616
with the same helper call so both handlers use one implementation.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: c589c86f-972b-48df-96dc-b348f4d1ae25
📒 Files selected for processing (14)
docs-site/src/content/docs/guides/providers.mddocs-site/src/content/docs/ja/guides/providers.mddocs-site/src/content/docs/ko/guides/providers.mddocs-site/src/content/docs/ru/guides/providers.mddocs-site/src/content/docs/zh-cn/guides/providers.mdsrc/providers/registry.tssrc/router.tssrc/server/adapter-resolve.tssrc/server/chat-completions.tssrc/server/claude-messages.tssrc/server/responses/core.tstests/github-copilot-responses-wire-recovery.test.tstests/github-copilot-wire-defaults.test.tstests/router.test.ts
CodeRabbit follow-up review — both findings applied (
|
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/server/responses/core.ts (1)
1543-1549: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winPropagate refreshed Kiro auth context before replay.
The new replay updates only
route.provider.apiKey, butpassthroughAdapter.buildRequest(parsed, ...)also consumesparsed._kiroAuthContext. The existing recovery path updates this field fromrefreshed.kiroat Lines 2237-2239; without the same update here, a Kiro OAuth refresh can replay with stale context and fail again.🔧 Proposed fix
sentOAuthSnapshot = refreshed; + if (route.providerName === "kiro") { + parsed._kiroAuthContext = { ...(refreshed.kiro ?? {}) }; + } nextProvider = resolveProviderTransport(As per path instructions,
src/**changes must flag provider/adapter contract drift and preserve shared routing/config behavior.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/server/responses/core.ts` around lines 1543 - 1549, Update the OAuth refresh replay path around sentOAuthSnapshot and nextProvider to also propagate refreshed.kiro into parsed._kiroAuthContext before passthroughAdapter.buildRequest(parsed, ...); preserve the existing route.provider apiKey update, provider transport resolution, and shared routing/config behavior.Source: Path instructions
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@src/server/responses/core.ts`:
- Around line 1543-1549: Update the OAuth refresh replay path around
sentOAuthSnapshot and nextProvider to also propagate refreshed.kiro into
parsed._kiroAuthContext before passthroughAdapter.buildRequest(parsed, ...);
preserve the existing route.provider apiKey update, provider transport
resolution, and shared routing/config behavior.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 52683971-e920-4ef5-baf6-5b8f22962172
📒 Files selected for processing (4)
src/server/chat-completions.tssrc/server/claude-messages.tssrc/server/responses-wire-params.tssrc/server/responses/core.ts
|
NEEDS-SECURITY-REVIEW — blocked on review, not on changes. This is a bigger change than the title suggests, and that is why it needs a reviewer rather than a merge. Beyond Replay-after-credential-change is the part that needs someone hostile looking at it. The questions a reviewer should be able to answer from the code:
Your tests cover wire selection, override precedence, destination and credential preservation, OAuth replay, pool rotation, and non-Copilot isolation. That is a genuinely useful starting point for the reviewer — it is not a substitute for the approval. What happens next: request explicit security review. No changes are being asked for ahead of it. CI context (shared across the current review round). |
The dev checkout is mid-optimization, so this round stages on codex/260731-pr-merge-round instead of dev. The user merges it into dev and releases once that unit lands. Four reviewers triaged every open PR against origin/dev=356924263 on disjoint slices, and the load-bearing finding is that seven PRs are already fixed on dev by different commits. Merging those heads now would REVERT the newer work. lidge-jun#736 is the clearest case: dev carries decodeSchtasksOutput() at src/service.ts:364-393 because schtasks /query /xml emits UTF-16LE, and the PR head deletes that block and restores encoding: "utf8" -- landing it reopens lidge-jun#722. A clean merge-tree is not permission to merge. An adversarial audit then returned five blockers, all folded in rather than argued away. Two changed what actually gets merged. lidge-jun#744 was routed as a routine catalog fix, but 59d95c0 and 39543a3 change OAuth reconciliation, persist provider settings, and move token resolution around the static branch -- security review per MAINTAINERS.md, so it left the batch. lidge-jun#781's topic commit also swaps three /api/logs test files onto a logsFromApiBody helper that accepts both the array and the {logs} envelope; adopting it would pre-accept the very contract batch B rejects, so only the Anthropic file and its own test come across. The audit also caught three PRs missing from a matrix that claimed to be complete (lidge-jun#750, lidge-jun#746, lidge-jun#644), and lidge-jun#644 additionally carries .codexclaw/goalplans/** and two .DS_Store files. Nothing is merged yet. Batch A is six PRs, batch B rebuilds three whose implementations no longer fit the tree, and 20+ are held for security review, provider evidence, or their own cycle -- each with what would unblock it recorded.
|
@mushikingh Pleasse resolve conflicts & CI Errors, then we will security review it. |
…ompletions Copilot fronts a mixed-wire catalog. The preset adapter `openai-chat` is correct for its Claude, Gemini, GPT-3.5/4 and gpt-5-mini entries, but the newer OpenAI models are Responses-only on this upstream and fail with model "gpt-5.6-sol" is not accessible via the /chat/completions endpoint gpt-5.4 hides the same problem behind a passing smoke test: a text-only chat request succeeds and only a real Codex request fails, because Codex supplies function tools plus a reasoning effort. Declare the seven affected models in the registry's `modelWireDefaults`, so they resolve to `openai-responses` without every user hand-writing the same `modelAdapters` block. Unlike DeepSeek's Flash entry these are bare strings rather than inbound-scoped: the upstream serves no chat route at all for them, so a Chat Completions or Claude Code client has to be translated too instead of being kept on the wire it already speaks. Also refresh the Copilot cold-start seed from the verified live catalog — the old seed listed gpt-4.1-mini and claude-sonnet-4, neither of which it contains — so a qualified selector resolves before live discovery lands. Separately, bare OpenAI-family ids are reserved for the canonical `openai` provider, so `-m gpt-5.6-sol` cannot mean Copilot. NoEnabledOpenAiProviderError now names a qualified selector that would work when another enabled provider serves that id. Registry `modelWireDefaults` and configured `modelAdapters` keys also feed knownModelIdsForProvider, so a provider naming a model only there is selectable as `provider/model`. Verified 2026-07-30 against a live 37-entry Copilot catalog with real `codex exec` runs rather than minimal text probes: presence in GET /models proves neither, and a text-only success does not prove the model survives function tools plus reasoning_effort.
…s gateways Two defects that a registry `modelWireDefaults` entry makes reachable: putting a model on the Responses wire moves it onto code paths written for the canonical ChatGPT forward provider. Sampling params. Both translate-and-replay inbound handlers stripped max_output_tokens for ANY openai-responses route, though the intent — stated in claude-messages.ts as "routed providers keep them" — was the native ChatGPT forward path only. Gate it on `authMode === "forward"`, matching the adapter's own stripUnsupportedForwardParams condition so the two layers agree, and extract the shared block both handlers had been carrying in duplicate. temperature/top_p/stop/user stay stripped on every Responses route: `stop` is not a Responses parameter, reasoning models reject temperature/top_p, and noTemperatureModels is honored only by the openai-chat adapter, so there is no per-model filter on this wire. Credential recovery. The passthrough branch formats non-2xx immediately, while the OAuth 401 refresh and key-pool 429 rotation live only in the normal adapter loop — so a model routed onto this wire would surface a 401 a refresh fixes, or a 429 a healthy pool key absorbs, while its chat-wire siblings on the same provider recover. Add a bounded recovery before the error formatting: at most one forced refresh, then one replay per remaining pool key, both guards monotonic so a persistently failing upstream cannot loop. Nothing has streamed and the body is a replayable string, the same precondition the transient-5xx retry relies on. The failed response's socket is released before the refresh round-trip, not only before the replay, since a failed refresh returns early. The rotated credential is rebuilt through resolveProviderTransport under the SAME provider name, so it can only be addressed at that provider's validated destination, and the request is rebuilt from the rotated provider so the new credential replaces the old one everywhere it is carried. The canonical ChatGPT forward provider is excluded entirely, leaving its own pool-retry path untouched. Replays record oauth-401 / key-429 on the attempt so the request log explains why the send count grew.
5e4bf38 to
17e5263
Compare
Rebased onto current
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 17e5263f87
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| modelWireDefaults: { | ||
| "gpt-5.3-codex": "openai-responses", | ||
| "gpt-5.4": "openai-responses", | ||
| "gpt-5.4-mini": "openai-responses", |
There was a problem hiding this comment.
Route Copilot GPT-5.4 nano through Responses
When Copilot exposes gpt-5.4-nano (GitHub lists GPT-5.4 nano as a current Copilot/Codex model, and Pi's Copilot metadata marks its API as openai-responses), this exact default table misses it: github-copilot/gpt-5.4-nano is a qualified selector, so routing reaches Copilot, but resolveWireProtocolOverride finds no default and keeps openai-chat, causing the same /chat/completions failure this patch fixes for gpt-5.4-mini; add the nano id to both the seed and this map.
Useful? React with 👍 / 👎.
Closes #748
Problem
The
github-copilotpreset configures a provider-wideopenai-chatadapter. That is correct for most of the Copilot catalog, but Copilot fronts a mixed-wire catalog and several newer OpenAI models are only served by the Responses API:gpt-5.4hides the same problem behind a passing smoke test. A minimal text-only Chat Completions request succeeds; a real Codex request fails, because Codex supplies function tools and a reasoning effort:modelAdaptersalready solves this, but every Copilot user has to discover the failure and hand-write the same map. The gap is the missing built-in mapping.Change
Built-in per-model wire defaults — new
src/providers/model-wire-defaults.tsholds a per-provider default map, consulted fromresolveWireProtocolOverride. Precedence becomes:Design notes:
modelAdaptersentry always wins, including one that names the provider-wide adapter — that is how a user opts a model back out of a default. The previousrequested !== providerConfig.adaptershort-circuit would have silently dropped such an entry into the default path, so the override is now resolved before that comparison.MODEL_ADAPTER_OVERRIDE_ALLOWED), so they never override a provider a user moved to some other adapter.types.ts: a pin means the upstream speaks exactly one wire and a user override must not apply. These are defaults.gpt-5.4-2026-07-30) resolve the same way — Copilot publishes dated ids (gpt-4o-2024-05-13).Models routed to
openai-responses:gpt-5.3-codex,gpt-5.4,gpt-5.4-mini,gpt-5.5,gpt-5.6-luna,gpt-5.6-sol,gpt-5.6-terra.Cold-start seed refresh — the Copilot registry seed listed
gpt-4.1-miniandclaude-sonnet-4, neither of which is in the live catalog. Refreshed from the verified catalog (embeddings and the internaltrajectory-compactionentry excluded — they are not chat models) so a qualified selector resolves before live discovery lands.Selector error hint — bare OpenAI-family ids are reserved for the canonical
openaiprovider, so-m gpt-5.6-solcannot mean Copilot and fails withModel gpt-5.6-sol requires the canonical openai provider.NoEnabledOpenAiProviderErrornow also names a qualified selector that would work when another enabled provider serves that id, instead of only suggestingocx provider add openai. Message-only; routing is unchanged.Verification
Field verification was done on OpenCodex
2.7.43/ Codex CLI0.145.0against a live 37-entry Copilot catalog, with realcodex execruns rather than minimal text probes — presence inGET /modelsdoes not prove a model works on/chat/completions, and a text-only success does not prove it survives function tools plusreasoning_effort. All 33 generative models returned successfully under the resulting mapping; before it, the seven above returnedunsupported_api_for_modelor the function-tools/reasoning incompatibility error.In-repo:
bun run typecheckclean.tests/github-copilot-wire-defaults.test.tscovers both wire groups, dated snapshots, explicit-override precedence in both directions, an out-of-allow-list override falling through to the default, resolve idempotence, credential/destination survival across the swap, non-OpenAI-wire providers being left alone, and seed coverage.tests/adapter-resolve.test.ts(defaults do not leak to other providers) andtests/router.test.ts(the selector hint).tests/{adapter-resolve,router,reasoning-effort,effort-policy}.test.ts+ the new file: 122 pass / 0 fail. The fullbun run testsuite was not run locally — please let CI be the gate.Not changed
With
adapter: "openai-responses"and noresponsesPath, the URL builds ashttps://api.githubcopilot.com/v1/responses. Copilot's own documented path is/responses. The field verification reported HTTP 200 on real Codex runs through/v1/responses, so Copilot evidently accepts both and I left it alone rather than guess without a Copilot subscription to test against. Worth a follow-up if a maintainer can confirm.Maintenance
The map needs a recheck whenever Copilot adds or changes models; the rationale and the verification caveat are recorded in the source comment. Automatic derivation from live discovery (if Copilot exposes per-model
supported_endpoints) would be the durable fix and is deliberately out of scope here.Docs
English
guides/providers.mdgains a "GitHub Copilot model wires" section (affected models, both error shapes, the opt-out example, and the qualified selector);reference/configuration.mdnotes built-in defaults on themodelAdaptersrow. ko/ja/zh-cn/ru get matching notes so the locales do not contradict the English source.Summary by CodeRabbit
New Features
Bug Fixes
Documentation
modelAdaptersprecedence.Tests