Skip to content

fix(api-proxy): route GPT-5 family models to /responses regardless of auth path#3177

Merged
lpcox merged 4 commits into
mainfrom
copilot/awf-api-proxy-fix-route-issue
May 15, 2026
Merged

fix(api-proxy): route GPT-5 family models to /responses regardless of auth path#3177
lpcox merged 4 commits into
mainfrom
copilot/awf-api-proxy-fix-route-issue

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented May 15, 2026

Bug Fix

What was the bug?

Workflows using engine.id: copilot with a GPT-5/O3-family model (e.g. gpt-5.4-mini) and standard GitHub OAuth auth (COPILOT_GITHUB_TOKEN) failed with 400 model "gpt-5.4-mini" is not accessible via the /chat/completions endpoint. The COPILOT_PROVIDER_WIRE_API=responses agent env var was gated entirely inside the if (config.copilotApiKey) branch, so the Copilot CLI defaulted to /chat/completions whenever only copilotGithubToken was provided.

A secondary issue: when COPILOT_API_KEY in the sidecar environment is the AWF placeholder sentinel (ghu_aaa…), the adapter's apiKey variable was truthy, incorrectly activating BYOK-specific code paths (e.g. model fetching from custom providers with the dummy key).

How did you fix it?

src/services/api-proxy-service.ts — Moved the requiresResponsesWireApi() check out of the copilotApiKey-only branch into the shared copilotGithubToken || copilotApiKey block. The wire API is now model-driven, not auth-path-driven:

// Before — only ran when copilotApiKey was set
if (config.copilotApiKey) {
  ...
  const copilotModel = getCopilotModel(config);
  if (copilotModel && requiresResponsesWireApi(copilotModel)) {
    agentEnvAdditions.COPILOT_PROVIDER_WIRE_API = 'responses';
  }
}

// After — runs for any Copilot credential
if (config.copilotGithubToken || config.copilotApiKey) {
  ...
  const copilotModel = getCopilotModel(config);
  if (copilotModel && requiresResponsesWireApi(copilotModel)) {
    agentEnvAdditions.COPILOT_PROVIDER_WIRE_API = 'responses';
  }
}

containers/api-proxy/providers/copilot.js — Added resolveApiKey() which returns undefined when COPILOT_API_KEY equals the known AWF placeholder constant, preventing the sidecar from treating it as a real BYOK credential. createCopilotAdapter now uses resolveApiKey() in place of the raw stripBearerPrefix call for apiKey.

Testing

  • New tests in api-proxy-service.test.ts cover COPILOT_PROVIDER_WIRE_API=responses being set for gpt-5, gpt-5.4-mini, o3-mini, etc. when only copilotGithubToken is provided.
  • New tests in server.auth.test.js cover resolveApiKey() returning undefined for the placeholder and the adapter correctly falling back to COPILOT_GITHUB_TOKEN when COPILOT_API_KEY is the sentinel.

Copilot AI added 2 commits May 15, 2026 02:51
… auth path

- In api-proxy-service.ts, decouple COPILOT_PROVIDER_WIRE_API=responses from
  the copilotApiKey branch: it is now set based solely on the detected model
  via requiresResponsesWireApi() whenever any Copilot credential is configured
  (copilotGithubToken OR copilotApiKey). This fixes the 400 error reported when
  using gpt-5.4-mini with COPILOT_GITHUB_TOKEN in strict AWF mode.

- In copilot.js, introduce resolveApiKey() which filters out the AWF placeholder
  token (ghu_aaa...) so the sidecar never treats it as a real BYOK credential.
  createCopilotAdapter now uses resolveApiKey() for the apiKey variable so that
  when AWF injects the placeholder as COPILOT_API_KEY, the sidecar falls back to
  COPILOT_GITHUB_TOKEN as the sole auth source and BYOK-specific code paths
  (e.g. model fetching from custom providers) are not triggered.

- Add tests covering the GitHub token wire-API path (gpt-5, o3-mini, gpt-5.4-mini)
  and the placeholder-key skipping behavior in the sidecar adapter.

Fixes: api-proxy: gpt-5.4-mini routed to /chat/completions instead of /responses
Copilot AI changed the title [WIP] Fix gpt-5.4-mini routing to /responses in strict mode fix(api-proxy): route GPT-5 family models to /responses regardless of auth path May 15, 2026
Copilot AI requested a review from lpcox May 15, 2026 02:55
Copilot finished work on behalf of lpcox May 15, 2026 02:55
@lpcox lpcox marked this pull request as ready for review May 15, 2026 02:58
Copilot AI review requested due to automatic review settings May 15, 2026 02:58
@lpcox lpcox requested a review from Mossaka as a code owner May 15, 2026 02:58
@github-actions

This comment has been minimized.

@github-actions github-actions Bot mentioned this pull request May 15, 2026
@github-actions

This comment has been minimized.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes Copilot API proxy wiring so GPT-5/O3-family models reliably use the /responses wire API even when the workflow is authenticated via COPILOT_GITHUB_TOKEN (OAuth path), and prevents the api-proxy sidecar from treating the AWF placeholder sentinel as a real BYOK key.

Changes:

  • Set COPILOT_PROVIDER_WIRE_API=responses based on COPILOT_MODEL whenever any Copilot credential is configured (GitHub token or API key), not only in BYOK mode.
  • Add resolveApiKey() in the Copilot provider adapter to treat the AWF placeholder COPILOT_API_KEY value as “absent”, avoiding incorrect BYOK-only behavior.
  • Add/extend unit tests covering the above behaviors for GitHub-token mode and placeholder handling in the adapter.
Show a summary per file
File Description
src/services/api-proxy-service.ts Moves responses-wire-api selection to the shared Copilot-credential path so GPT-5/O3 models route correctly regardless of auth mode.
src/services/api-proxy-service.test.ts Adds test coverage ensuring COPILOT_PROVIDER_WIRE_API=responses is set (or not set) in GitHub token mode based on model.
containers/api-proxy/server.auth.test.js Adds tests for placeholder-aware API key resolution and adapter enablement/auth-header behavior.
containers/api-proxy/providers/copilot.js Introduces resolveApiKey() to ignore the placeholder sentinel and exports it for tests.

Copilot's findings

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comments suppressed due to low confidence (2)

containers/api-proxy/server.auth.test.js:271

  • Test description refers to "no GITHUB_TOKEN", but the adapter enablement logic here depends on COPILOT_GITHUB_TOKEN. Updating the wording to COPILOT_GITHUB_TOKEN would make the test intent accurate and easier to understand.
  it('is disabled when COPILOT_API_KEY is the AWF placeholder and no GITHUB_TOKEN is set', () => {
    const adapter = createCopilotAdapter({ COPILOT_API_KEY: COPILOT_PLACEHOLDER_TOKEN });
    expect(adapter.isEnabled()).toBe(false);
  });

containers/api-proxy/server.auth.test.js:279

  • This test case description mentions "GITHUB_TOKEN", but the input variable that enables the adapter in this scenario is COPILOT_GITHUB_TOKEN. Consider renaming the test to avoid ambiguity with the standard GitHub Actions GITHUB_TOKEN.
  it('is enabled when COPILOT_API_KEY is the AWF placeholder but COPILOT_GITHUB_TOKEN is set', () => {
    const adapter = createCopilotAdapter({
      COPILOT_GITHUB_TOKEN: 'gho_real_token',
      COPILOT_API_KEY: COPILOT_PLACEHOLDER_TOKEN,
    });
    expect(adapter.isEnabled()).toBe(true);
  });
  • Files reviewed: 4/4 changed files
  • Comments generated: 2

Comment on lines +26 to +30
// AWF injects this sentinel value into the agent environment for credential isolation.
// The ghu_ prefix is intentional: it matches the GitHub token shape that Copilot CLI
// auth pre-checks expect, but the 36 repeated 'a' characters make it unambiguous as
// a non-real placeholder. It is defined in src/constants/placeholders.ts and must
// stay in sync. When this value appears as COPILOT_API_KEY in the sidecar environment
})).toBe('gho_abc123');
});

it('treats AWF placeholder COPILOT_API_KEY as absent when no GITHUB_TOKEN is set', () => {
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@lpcox
Copy link
Copy Markdown
Collaborator

lpcox commented May 15, 2026

@copilot address the review feedback

@github-actions

This comment has been minimized.

…ix test descriptions

- Add a test in placeholders.test.ts asserting that the hardcoded
  COPILOT_PLACEHOLDER_TOKEN in copilot.js matches the canonical TS constant,
  mirroring the existing health-check shell script consistency check.
- Rename three test cases in server.auth.test.js from "no GITHUB_TOKEN" to
  "no COPILOT_GITHUB_TOKEN" to match the actual environment variable name.
Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 15, 2026

@copilot address the review feedback

Both issues addressed in the latest commit:

  1. Placeholder drift protection — Added a test in src/constants/placeholders.test.ts that reads containers/api-proxy/providers/copilot.js and asserts its hardcoded COPILOT_PLACEHOLDER_TOKEN matches the canonical TS constant, mirroring the existing health-check shell script consistency check.

  2. Test description accuracy — Renamed the three test cases that said "no GITHUB_TOKEN" to "no COPILOT_GITHUB_TOKEN" to match the actual environment variable.

Copilot finished work on behalf of lpcox May 15, 2026 03:12
@github-actions
Copy link
Copy Markdown
Contributor

Smoke Test Results

GitHub API - gh pr list failed with HTTP 401 (Bad credentials)
Playwright - Navigated to https://github.com, title contains "GitHub"
File verify - /tmp/gh-aw/agent/smoke-test-claude-25898142486.txt exists

Result: FAIL (2/3 tests passed — GitHub API auth issue)

💥 [THE END] — Illustrated by Smoke Claude

@github-actions
Copy link
Copy Markdown
Contributor

Smoke Test: Copilot BYOK (Offline) Mode

Test Result
GitHub MCP connectivity ❌ 401 Bad credentials
GitHub.com HTTP ⚠️ Pre-step data not available (template unexpanded)
File write/read ⚠️ Pre-step data not available (template unexpanded)
BYOK inference (this response) ✅ Agent responding via api-proxy → api.githubcopilot.com

Running in BYOK offline mode (COPILOT_OFFLINE=true) via api-proxy → api.githubcopilot.com.

Overall: FAIL — GitHub MCP returned 401 and workflow template variables were not expanded, preventing full validation.

🔑 BYOK report filed by Smoke Copilot BYOK

@github-actions
Copy link
Copy Markdown
Contributor

🔬 Smoke Test Results

Test Status
GitHub MCP connectivity ❌ (401 Bad credentials)
GitHub.com HTTP ❌ (template variable not expanded)
File write/read ❌ (template variable not expanded)

Overall: FAIL

The pre-computed step outputs (steps.smoke-data.outputs.*) were not expanded — workflow template variables reached the agent as literal strings. GitHub MCP returned 401 errors.

/cc @Copilot

📰 BREAKING: Report filed by Smoke Copilot

@github-actions
Copy link
Copy Markdown
Contributor

Smoke Test Codex

GitHub PR review ✅ Refactor option parser utilities into domain-focused modules with compatibility wrappers; Refactor API proxy request path into focused guard and transport modules
SafeInputs GH ❌ tool unavailable
Playwright ✅ title contains GitHub
Tavily ❌ tool unavailable
File/Bash ✅; Discussion ⚠️ skipped; Build ✅
Overall status: FAIL

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • registry.npmjs.org

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "registry.npmjs.org"

See Network Configuration for more information.

🔮 The oracle has spoken through Smoke Codex

@github-actions
Copy link
Copy Markdown
Contributor

Chroot Smoke Test Results

Runtime Host Version Chroot Version Match?
Python Python 3.12.13 Python 3.12.3
Node.js v24.14.1 v20.20.2
Go go1.22.12 go1.22.12

Result: FAILED — Python and Node.js versions differ between host and chroot.

Tested by Smoke Chroot

@github-actions
Copy link
Copy Markdown
Contributor

Gemini Smoke Test Results - PASS

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • localhost

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "localhost"

See Network Configuration for more information.

💎 Faceted by Smoke Gemini

@github-actions
Copy link
Copy Markdown
Contributor

🏗️ Build Test Suite Results

Ecosystem Project Build/Install Tests Status
Bun elysia 1/1 passed ✅ PASS
Bun hono 1/1 passed ✅ PASS
C++ fmt N/A ✅ PASS
C++ json N/A ✅ PASS
Deno oak N/A 1/1 passed ✅ PASS
Deno std N/A 1/1 passed ✅ PASS
.NET hello-world N/A ✅ PASS
.NET json-parse N/A ✅ PASS
Go color 1/1 passed ✅ PASS
Go env 1/1 passed ✅ PASS
Go uuid 1/1 passed ✅ PASS
Java gson 1/1 passed ✅ PASS
Java caffeine 1/1 passed ✅ PASS
Node.js clsx All passed ✅ PASS
Node.js execa All passed ✅ PASS
Node.js p-limit All passed ✅ PASS
Rust fd 1/1 passed ✅ PASS
Rust zoxide 1/1 passed ✅ PASS

Overall: 8/8 ecosystems passed — ✅ PASS

Generated by Build Test Suite for issue #3177 · ● 5.4M ·

@github-actions
Copy link
Copy Markdown
Contributor

Smoke Test Results — FAIL

Check Result
Redis PING ❌ Timeout (no response)
PostgreSQL pg_isready ❌ No response on port 5432
PostgreSQL SELECT 1 ❌ Not reached

Overall: FAILhost.docker.internal is not reachable from this runner environment. Service containers appear unavailable.

🔌 Service connectivity validated by Smoke Services

@lpcox lpcox merged commit a57ce18 into main May 15, 2026
65 of 68 checks passed
@lpcox lpcox deleted the copilot/awf-api-proxy-fix-route-issue branch May 15, 2026 03:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[awf] api-proxy: gpt-5.4-mini routed to /chat/completions instead of /responses in strict mode

3 participants