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:
@@ -1076,6 +1076,28 @@ pub const CLAUDE_CODE_USER_AGENT: &str = "claude-cli/2.1.75";
|
||||
pub const CLAUDE_CODE_SYSTEM_PREFIX: &str =
|
||||
"You are Claude Code, Anthropic's official CLI for Claude.";
|
||||
|
||||
// ── GitHub Copilot editor-identity headers ──────────────────────────────────
|
||||
// The VS Code Copilot Chat client identity. Copilot's proxy authorizes the
|
||||
// short-lived copilot token AND validates these editor headers, so they ride
|
||||
// the `copilot_internal/v2/token` exchange, the `/models` listing, and every
|
||||
// `/chat/completions` inference request. github-copilot-GATED: no other
|
||||
// platform sends them, so their requests stay byte-identical. Values are
|
||||
// non-secret wire constants (ported from Pi `api/github-copilot-headers.ts` +
|
||||
// `auth/oauth/github-copilot.ts`).
|
||||
|
||||
/// `User-Agent` for the Copilot path (overrides the default kigi UA, OAuth-gated).
|
||||
pub const COPILOT_USER_AGENT: &str = "GitHubCopilotChat/0.35.0";
|
||||
/// `Editor-Version` — the host editor Copilot believes it is talking to.
|
||||
pub const COPILOT_EDITOR_VERSION: &str = "vscode/1.107.0";
|
||||
/// `Editor-Plugin-Version` — the Copilot Chat plugin build.
|
||||
pub const COPILOT_EDITOR_PLUGIN_VERSION: &str = "copilot-chat/0.35.0";
|
||||
/// `Copilot-Integration-Id` — the integration the token is scoped to.
|
||||
pub const COPILOT_INTEGRATION_ID: &str = "vscode-chat";
|
||||
/// `X-GitHub-Api-Version` — sent ONLY on the `/models` listing.
|
||||
pub const COPILOT_API_VERSION: &str = "2026-06-01";
|
||||
/// `X-Initiator` value — sent ONLY on inference (`user`, per the spec).
|
||||
pub const COPILOT_INITIATOR: &str = "user";
|
||||
|
||||
/// ChatCompletions request-body adaptation dialect. Providers disagree on
|
||||
/// how thinking rides an OpenAI-compatible body: Kimi wants
|
||||
/// `thinking:{type,effort}`, DeepSeek wants
|
||||
|
||||
Reference in New Issue
Block a user