feat(providers): add GitHub Copilot subscription OAuth (device flow + copilot-token re-mint)
28th platform `github-copilot` (uses_oauth, ChatCompletions wire). Two-stage auth: RFC-8628 GitHub device flow (client Iv1.b507a08c87ecfe98, scope read:user, errors in a 200 body) mints the DURABLE github token; a GET api.github.com/copilot_internal/ v2/token exchange re-mints the SHORT-LIVED copilot token. Persisted as key=copilot token, refresh_token=github token, expires_at=copilot expiry; the "refresh" is a copilot-token re-mint (not a refresh_token grant), dispatched via OAuthTokenBody::GithubCopilotExchange in the generic refresher. VS Code editor-identity headers on /models + /chat/completions, gated on SamplerConfig.github_copilot / PlatformId::sends_copilot_editor_headers() so every other ChatCompletions provider stays byte-identical. Live /models filtered (parse_github_copilot_listing) to the openai-completions-served models: keep iff model_picker_enabled && policy.state!="disabled" && tool_calls!=false AND not a claude-4.x/5.x (messages) or gpt-5/oswe/mai- (responses-only) id — those need per-model wire routing (documented debt), excluded rather than mis-routed. Inherits the leak-safe pooled routing (scope oauth/github-copilot); its bearer/ refresh/api_key never touch the Kimi token (regression test added). Fail-fast on an out-of-range copilot expires_at (would otherwise silently 401 mid-session). Adversarial security review: GO, no CRITICAL/HIGH. Known limitation: Pi's per-model policy-enablement POST is not ported (documented in AGENTS.md).
This commit is contained in:
1 parent
5a9183b08b
commit
8179438278
25 files changed
+1432
-44
No files matched your search
@@ -4091,6 +4091,14 @@ pub fn sampling_config_for_model(
|
||||
platform.oauth().is_some()
|
||||
&& platform.wire_api() == kigi_models::PlatformWireApi::Messages
|
||||
});
|
||||
// GitHub Copilot editor-identity headers: a managed key whose platform is
|
||||
// github-copilot drives the editor headers + X-Initiator in the sampler.
|
||||
// Gated here so every other ChatCompletions platform stays byte-identical.
|
||||
let github_copilot = info
|
||||
.id
|
||||
.as_deref()
|
||||
.and_then(kigi_models::parse_managed_model_key)
|
||||
.is_some_and(|(platform, _)| platform.sends_copilot_editor_headers());
|
||||
SamplerConfig {
|
||||
api_key: credentials.api_key,
|
||||
model: model_name,
|
||||
@@ -4101,6 +4109,7 @@ pub fn sampling_config_for_model(
|
||||
api_backend,
|
||||
auth_scheme: credentials.auth_scheme,
|
||||
anthropic_oauth,
|
||||
github_copilot,
|
||||
chat_compat,
|
||||
extra_headers,
|
||||
context_window: info.context_window.get(),
|
||||
|
||||
Reference in new issue
Block a user