fix(copilot): route enterprise Copilot seats to the right endpoint and host - #93
Merged
Merged
Conversation
…d host A GitHub Copilot Business/Enterprise login failed every turn with `421 Misdirected Request`, whatever the prompt was. Root cause: an enterprise seat's `/models` list carries ids Pi's bundled catalog doesn't have yet (the `gpt-5.6-*` family). The never-miss resolver synthesizes an unknown id by cloning a catalogued model of the same provider — but it cloned the FIRST one, `claude-fable-5`, an `openai-completions` entry. Copilot serves GPT-5+/codex only on `/responses`, so every request went to `/chat/completions` and the edge answered 421. Two aggravating factors on the same path: the bundled catalog hardcodes `api.individual.githubcopilot.com` (a business or enterprise token must use its own host), and 421 classified as `unknown` — transient — so the loop burned three identical retries and surfaced "last reason=unknown". GitHub advertises no endpoint metadata in `/models` (github/copilot-cli#4337), so endpoint selection resolves newest-evidence first: learned (what the live API accepted) -> advertised (parsed defensively, so it activates itself if GitHub ever ships the field) -> id family (what Pi's catalog encodes and GitHub's own clients assume). - new `github-copilot-transport.ts`: endpoint + host + editor-header correction, applied at one choke point in the agent loop so the catalogued and synthesized paths are both covered - self-healing stream wrapper: an endpoint rejection before any event is emitted re-issues once on the other surface and is remembered, so a model family shipped after this release routes itself. Bounded to Copilot, once per stream, never after partial output - host derived from the live token's `proxy-ep`, covering individual/business/enterprise and the GHE `copilot-proxy.` shape; a deliberately configured gateway baseUrl is left alone - synthesized Copilot ids take context window + vision from the account's own catalog instead of a template's - template selection is family-matched for every provider: `gpt-5.6-sol` clones `gpt-5.5`, not the provider's first-listed model - 421 and "not accessible via the /… endpoint" classify as model_not_found: one fast failure with an actionable message, and the fallback chain gets its turn
Bhasvanth-Spinabot
self-requested a review
August 7, 2026 16:35
Bhasvanth-Spinabot
approved these changes
Aug 7, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The bug
A GitHub Copilot Business/Enterprise login failed every turn with
421 Misdirected Request, whatever the prompt was.An enterprise seat's
/modelslist carries ids Pi's bundled catalog doesn't have yet (thegpt-5.6-*family). The never-miss resolver synthesizes an unknown id by cloning a catalogued model of the same provider — but it cloned the first one,claude-fable-5, anopenai-completionsentry. Copilot serves GPT-5+/codex only on/responses, so every request went to/chat/completionsand the edge answered 421.Two aggravating factors on the same path:
api.individual.githubcopilot.com, but a business/enterprise token must use its own host (encoded in the token'sproxy-ep);unknown— transient — so the loop burned three identical retries and surfacedlast reason=unknown.The approach
GitHub advertises no endpoint metadata in
/models(github/copilot-cli#4337), so endpoint selection resolves newest evidence first:/modelsentry, parsed defensively so it activates itself if GitHub ever ships the fieldChanges
github-copilot-transport.ts— endpoint + host + editor-header correction, applied at one choke point in the agent loop so the catalogued and synthesized paths are both coveredproxy-ep, covering individual/business/enterprise and the GHEcopilot-proxy.→copilot-api.shape; a deliberately configured gateway baseUrl is left alonegpt-5.6-solclonesgpt-5.5, not the provider's first-listed model421and "not accessible via the /… endpoint" classify asmodel_not_found: one fast failure with an actionable message, and the fallback chain gets its turnVerification
npm run buildclean;tsc --noEmitclean on both configsgithub-copilot-routing.test.ts, plus 6 classifier cases)References
gpt-5.6-lunalisted in/models, rejected on/chat/completions