diff --git a/src/services/api/openaiShim/requestExecutor.test.ts b/src/services/api/openaiShim/requestExecutor.test.ts index 9d3e05918e..742c9e86d3 100644 --- a/src/services/api/openaiShim/requestExecutor.test.ts +++ b/src/services/api/openaiShim/requestExecutor.test.ts @@ -758,6 +758,40 @@ test('raw-env LLMTR ignores unsupported custom auth and custom headers', async ( expect(captured.headers['X-Tenant-Secret']).toBeUndefined() }) +test('OpenCode Go sends the required stable session and user-agent headers', async () => { + process.env.CLAUDE_CODE_USE_OPENAI = '1' + process.env.OPENAI_BASE_URL = 'https://opencode.ai/zen/go/v1' + process.env.OPENAI_MODEL = 'glm-5.1' + process.env.OPENCODE_API_KEY = 'opencode-key' + delete process.env.OPENAI_API_KEY + delete process.env.OPENAI_API_KEYS + + const captured = await captureChatCompletionRequest('glm-5.1', { + 'x-opencode-session': 'caller-session', + 'User-Agent': 'caller-agent', + }) + + expect(captured.headers['x-opencode-session']).toMatch( + /^[0-9a-f-]{36}$/, + ) + expect(captured.headers['User-Agent']).toMatch(/^openclaude\//) + expect(captured.headers['x-opencode-session']).not.toBe('caller-session') + expect(captured.headers['User-Agent']).not.toBe('caller-agent') +}) + +test('OpenCode Zen does not receive the OpenCode Go session header', async () => { + process.env.CLAUDE_CODE_USE_OPENAI = '1' + process.env.OPENAI_BASE_URL = 'https://opencode.ai/zen/v1' + process.env.OPENAI_MODEL = 'gpt-5.4' + process.env.OPENCODE_API_KEY = 'opencode-key' + delete process.env.OPENAI_API_KEY + delete process.env.OPENAI_API_KEYS + + const captured = await captureChatCompletionRequest('gpt-5.4') + + expect(captured.headers['x-opencode-session']).toBeUndefined() +}) + test('custom endpoints preserve configured auth and custom headers', async () => { process.env.CLAUDE_CODE_USE_OPENAI = '1' process.env.OPENAI_BASE_URL = 'https://proxy.example/v1' diff --git a/src/services/api/openaiShim/requestExecutor.ts b/src/services/api/openaiShim/requestExecutor.ts index 373c70b54c..7910fb2f35 100644 --- a/src/services/api/openaiShim/requestExecutor.ts +++ b/src/services/api/openaiShim/requestExecutor.ts @@ -1,6 +1,7 @@ import type { CredentialLease, CredentialPool } from '../credentialPool.js' import type { OpenAICompatibilityFailure } from '../openaiErrorClassification.js' import type { OpenAIShimRuntimeContext } from '../../../integrations/runtimeMetadata.js' +import { resolveRouteIdFromBaseUrl } from '../../../integrations/routeMetadata.js' import { getCommandcodeChatCompletionsModelError } from '../../../integrations/gateways/commandcode.js' import { redactEncodedSecretSubstringsForDisplay, @@ -295,7 +296,10 @@ export async function executeOpenAIRequest( const isXaiRoute = runtimeShimContext.routeId === 'xai' || isXaiBaseUrl(request.baseUrl) const openCodeGoSessionId = - runtimeShimContext.routeId === 'opencode-go' ? getSessionId() : null + (runtimeShimContext.routeId === 'opencode-go' || + resolveRouteIdFromBaseUrl(request.baseUrl) === 'opencode-go') + ? getSessionId() + : null const openAIApiKeysPoolRaw = routeAcceptsGenericOpenAICredentials && parseCredentialList(requestProcessEnv.OPENAI_API_KEYS).length > 0