diff --git a/containers/api-proxy/copilot-byok.test.js b/containers/api-proxy/copilot-byok.test.js index fc0ef6255..e17e63c15 100644 --- a/containers/api-proxy/copilot-byok.test.js +++ b/containers/api-proxy/copilot-byok.test.js @@ -195,6 +195,38 @@ describe('createCopilotAdapter — BYOK getAuthHeaders', () => { expect(config.apiVersion).toBe('2026-07-01'); }); + it.each([ + ['/auto', '2026-08-01'], + ['/models/session', '2025-07-16'], + ['/models/session/intent', '2025-07-16'], + ])('injects the required API version for POST %s', (url, expectedVersion) => { + const adapter = createCopilotAdapter({ COPILOT_GITHUB_TOKEN: githubToken }); + const headers = adapter.getAuthHeaders({ url, method: 'POST', headers: {} }); + + expect(headers['X-GitHub-Api-Version']).toBe(expectedVersion); + }); + + it('preserves a caller-supplied API version for /auto', () => { + const adapter = createCopilotAdapter({ COPILOT_GITHUB_TOKEN: githubToken }); + const headers = adapter.getAuthHeaders({ + url: '/auto', + method: 'POST', + headers: { 'x-github-api-version': '2027-01-01' }, + }); + + expect(headers['X-GitHub-Api-Version']).toBeUndefined(); + }); + + it('does not inject GitHub API versions into custom BYOK targets', () => { + const adapter = createCopilotAdapter({ + COPILOT_PROVIDER_API_KEY: byokKey, + COPILOT_API_TARGET: 'router.example.com', + }); + const headers = adapter.getAuthHeaders({ url: '/auto', method: 'POST', headers: {} }); + + expect(headers['X-GitHub-Api-Version']).toBeUndefined(); + }); + it('uses COPILOT_PROVIDER_API_KEY (not COPILOT_GITHUB_TOKEN) for inference in BYOK+token mode', () => { const adapter = createCopilotAdapter({ COPILOT_GITHUB_TOKEN: githubToken, diff --git a/containers/api-proxy/providers/copilot.js b/containers/api-proxy/providers/copilot.js index ab5617213..96cc1052f 100644 --- a/containers/api-proxy/providers/copilot.js +++ b/containers/api-proxy/providers/copilot.js @@ -34,12 +34,35 @@ const { resolveCopilotAuthToken, deriveCopilotApiTarget, copilotTargetRequiresGitHubTokenPrefix, + isGithubCopilotCatalogTarget, } = require('./copilot-auth'); const { bearerAuthHeaders, withCopilotIntegration } = require('./auth-headers'); const { URL } = require('url'); const { COPILOT_ENV } = require('../provider-env-constants'); const COPILOT_MODELS_API_VERSION = '2026-07-01'; +const COPILOT_LEGACY_AUTO_API_VERSION = '2025-07-16'; +const COPILOT_AUTO_API_VERSION = '2026-08-01'; + +function hasRequestHeader(req, name) { + return Object.keys(req.headers || {}).some(header => header.toLowerCase() === name.toLowerCase()); +} + +function getDefaultAutoApiVersion(req, pathname, rawTarget) { + if ( + req.method !== 'POST' + || hasRequestHeader(req, 'x-github-api-version') + || !isGithubCopilotCatalogTarget(rawTarget) + ) { + return undefined; + } + + if (pathname === '/auto') return COPILOT_AUTO_API_VERSION; + if (pathname === '/models/session' || pathname === '/models/session/intent') { + return COPILOT_LEGACY_AUTO_API_VERSION; + } + return undefined; +} /** * Create the GitHub Copilot provider adapter. @@ -261,7 +284,12 @@ function createCopilotAdapter(env, deps = {}) { return withCopilotIntegration({ 'Authorization': prefix + ' ' + githubToken }, integrationId); } - return resolveHeaders(); + const headers = resolveHeaders(); + const autoApiVersion = getDefaultAutoApiVersion(req, reqPathname, rawTarget); + if (autoApiVersion) { + headers['X-GitHub-Api-Version'] = autoApiVersion; + } + return headers; }, }); } diff --git a/containers/api-proxy/request-headers.test.js b/containers/api-proxy/request-headers.test.js index 934bdceca..3677c8ae3 100644 --- a/containers/api-proxy/request-headers.test.js +++ b/containers/api-proxy/request-headers.test.js @@ -35,6 +35,8 @@ describe('request-headers', () => { const req = { headers: { 'x-custom': 'keep-me', + 'copilot-session-token': 'session-jwt', + 'x-github-tenant': 'tenant-id', 'transfer-encoding': 'chunked', }, }; @@ -47,6 +49,8 @@ describe('request-headers', () => { }); expect(headers['x-initiator']).toBe('agent'); + expect(headers['copilot-session-token']).toBe('session-jwt'); + expect(headers['x-github-tenant']).toBe('tenant-id'); expect(headers['content-length']).toBe(String(body.length)); expect(headers['transfer-encoding']).toBeUndefined(); });