fix: enable Copilot HTTP transport fallback by ssncferreira · Pull Request #28494 · coder/coder · GitHub
Skip to content

fix: enable Copilot HTTP transport fallback - #28494

Merged
ssncferreira merged 5 commits into
mainfrom
fix/aibridge-copilot-ping-auth
Aug 27, 2026
Merged

fix: enable Copilot HTTP transport fallback#28494
ssncferreira merged 5 commits into
mainfrom
fix/aibridge-copilot-ping-auth

Conversation

@ssncferreira

@ssncferreira ssncferreira commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Description

Copilot provider model metadata returned by /models can advertise WebSocket support (for example, ws:/responses), so clients attempt WebSocket inference even though AI Gateway supports HTTP transport only. The Copilot CLI and Copilot in VS Code use different retry mechanisms, and only the CLI fallback worked previously. See the investigation details below for more information.

Authenticate every configured Copilot provider request with the Coder token validated during CONNECT while preserving Copilot provider credentials and preventing Coder credentials from being forwarded upstream. Reject unsupported WebSocket upgrades on bridged inference routes with 501 Not Implemented so clients can fall back to HTTP.

Changes

  • Add the CONNECT-authenticated Coder token to every configured Copilot provider request.
  • Preserve Copilot provider credentials and strip Coder credentials from provider credential headers.
  • Pass through Copilot /_ping connectivity checks and /auto model-selection requests.
  • Return 501 Not Implemented for WebSocket upgrades on bridged inference routes.
Investigation details

These logs show the behavior before this change using GPT-5.3-Codex, whose model metadata advertises ws:/responses.

Copilot CLI

  1. The CLI attempts a WebSocket upgrade. AI Gateway treats the empty GET /responses body as an inference request and returns 500.
2026-08-26 11:08:28.883 [info]  api: 2026-08-26 11:08:28.883 [warn]  coderd.ai-gateway.pool: failed to create interceptor  request_id=dcfc945c-fc10-4b8f-abc8-ae31aa21ad44  aibridgeproxy_id=6f651c82-68b2-45ee-b5e6-dc75445e2ef6  path=/copilot/responses ...
2026-08-26 11:08:28.883 [info]  api: error= unmarshal request body:
2026-08-26 11:08:28.883 [info]  api: github.com/coder/coder/v2/aibridge/provider.(*Copilot).CreateInterceptor
2026-08-26 11:08:28.883 [info]  api: /home/coder/coder/aibridge/provider/copilot.go:179
2026-08-26 11:08:28.883 [info]  api: - empty request body:
2026-08-26 11:08:28.883 [info]  api: github.com/coder/coder/v2/aibridge/intercept/responses.NewRequestPayload
2026-08-26 11:08:28.884 [info]  api: /home/coder/coder/aibridge/intercept/responses/reqpayload.go:48
2026-08-26 11:08:28.884 [info]  api: 2026-08-26 11:08:28.883 [warn]  coderd: GET  user_agent="copilot/1.0.80 (client/github/cli linux v24.18.1) term/unknown"  host=127.0.0.1:3000  received_host=127.0.0.1:3000  path=/api/v2/ai-gateway/copilot/responses  proto=HTTP/1.1  remote_addr=127.0.0.1  start="2026-08-26 11:08:28.880115732 +0000 UTC m=+402.868920240"  response_body="failed to create \"/copilot/responses\" interceptor\n"  took=3.644201ms  status_code=500  latency_ms=3  params_*=copilot/responses  request_id=dcfc945c-fc10-4b8f-abc8-ae31aa21ad44
2026-08-26 11:08:28.884 [info]  api: 2026-08-26 11:08:28.884 [erro]  coderd.aibridgeproxyd: received error response from aibridged  connect_id=17dda9cb-ae6a-416b-a812-b3b831755a2b  request_id=6f651c82-68b2-45ee-b5e6-dc75445e2ef6  provider=copilot  status=500  response_body="failed to create \"/copilot/responses\" interceptor\n"
  1. The CLI immediately retries inference with POST /responses, which succeeds.
2026-08-26 11:08:28.886 [info]  api: 2026-08-26 11:08:28.886 [debu]  coderd.aibridgeproxyd: request CONNECT authenticated  connect_id=7809a618-2c24-4f5f-a2f2-97af934b8c3e  host=api.business.githubcopilot.com:443  provider=copilot
2026-08-26 11:08:28.890 [info]  api: 2026-08-26 11:08:28.890 [info]  coderd.aibridgeproxyd: routing MITM request to AI Gateway  connect_id=7809a618-2c24-4f5f-a2f2-97af934b8c3e  request_id=eee98705-ffc2-4a00-8201-e316f997e19d  host=api.business.githubcopilot.com  method=POST  path=/responses  provider=copilot  gateway_target_url=http://127.0.0.1:3000/api/v2/ai-gateway/copilot/responses
2026-08-26 11:08:31.429 [info]  api: 2026-08-26 11:08:31.428 [debu]  coderd.aibridgeproxyd: received response from aibridged  connect_id=7809a618-2c24-4f5f-a2f2-97af934b8c3e  request_id=eee98705-ffc2-4a00-8201-e316f997e19d  provider=copilot  status=200

Copilot in VS Code

  1. VS Code attempts a WebSocket upgrade. AI Gateway treats the empty GET /responses body as an inference request and returns 500.
2026-08-26 11:09:20.736 [info]  api: 2026-08-26 11:09:20.736 [warn]  coderd.ai-gateway.pool: failed to create interceptor  request_id=4aab1c00-fb45-4533-b8c4-bcb150fd12f3  aibridgeproxy_id=d6867370-cefa-49bb-85ca-98dd4258e74c  path=/copilot/responses ...
2026-08-26 11:09:20.736 [info]  api: error= unmarshal request body:
2026-08-26 11:09:20.736 [info]  api: github.com/coder/coder/v2/aibridge/provider.(*Copilot).CreateInterceptor
2026-08-26 11:09:20.736 [info]  api: /home/coder/coder/aibridge/provider/copilot.go:179
2026-08-26 11:09:20.736 [info]  api: - empty request body:
2026-08-26 11:09:20.736 [info]  api: github.com/coder/coder/v2/aibridge/intercept/responses.NewRequestPayload
2026-08-26 11:09:20.736 [info]  api: /home/coder/coder/aibridge/intercept/responses/reqpayload.go:48
2026-08-26 11:09:20.736 [info]  api: 2026-08-26 11:09:20.736 [warn]  coderd: GET  user_agent=node  host=127.0.0.1:3000  received_host=127.0.0.1:3000  path=/api/v2/ai-gateway/copilot/responses  proto=HTTP/1.1  remote_addr=127.0.0.1  start="2026-08-26 11:09:20.732174884 +0000 UTC m=+454.720979381"  response_body="failed to create \"/copilot/responses\" interceptor\n"  took=4.225651ms  status_code=500  latency_ms=4  params_*=copilot/responses  request_id=4aab1c00-fb45-4533-b8c4-bcb150fd12f3
2026-08-26 11:09:20.737 [info]  api: 2026-08-26 11:09:20.736 [erro]  coderd.aibridgeproxyd: received error response from aibridged  connect_id=072a6497-7072-42d8-b52d-a3afe17d5a75  request_id=d6867370-cefa-49bb-85ca-98dd4258e74c  provider=copilot  status=500  response_body="failed to create \"/copilot/responses\" interceptor\n"
  1. VS Code checks connectivity with GET /_ping before retrying inference over HTTP. AI Gateway rejects the check because the proxy did not forward the Coder token from the authenticated CONNECT session.
2026-08-26 11:09:21.813 [info]  api: 2026-08-26 11:09:21.812 [info]  coderd.aibridgeproxyd: routing MITM request to AI Gateway  connect_id=2dd5a340-7ad9-43a6-b99d-fd34b838e6a2  request_id=8fda1e3e-4ca8-4908-a2ef-23859743dc38  host=api.business.githubcopilot.com  method=GET  path=/_ping  provider=copilot  gateway_target_url=http://127.0.0.1:3000/api/v2/ai-gateway/copilot/_ping
2026-08-26 11:09:21.813 [info]  api: 2026-08-26 11:09:21.813 [warn]  coderd.ai-gateway: no auth key provided  method=GET  path=/copilot/_ping  aibridgeproxy_id=8fda1e3e-4ca8-4908-a2ef-23859743dc38  request_id=057e3288-afa9-4f8d-9698-e4a3b206b199  aibridgeproxy_id=8fda1e3e-4ca8-4908-a2ef-23859743dc38
2026-08-26 11:09:21.814 [info]  api: 2026-08-26 11:09:21.814 [warn]  coderd.aibridgeproxyd: received error response from aibridged  connect_id=2dd5a340-7ad9-43a6-b99d-fd34b838e6a2  request_id=8fda1e3e-4ca8-4908-a2ef-23859743dc38  provider=copilot  status=400  response_body="no authentication key provided\n"

Closes https://linear.app/codercom/issue/AIGOV-629/ai-gateway-lacks-support-for-new-copilot-endpoints-behind-mitm

Note

Generated by Coder Agents on behalf of @ssncferreira.

@ssncferreira ssncferreira changed the title fix: authenticate Copilot connectivity pings fix: handle Copilot transport fallback Aug 24, 2026
@ssncferreira
ssncferreira force-pushed the fix/aibridge-copilot-ping-auth branch 3 times, most recently from 75ee752 to 13f882f Compare August 26, 2026 14:29
@ssncferreira
ssncferreira force-pushed the fix/aibridge-copilot-ping-auth branch from 13f882f to 473dbb0 Compare August 26, 2026 14:36
@ssncferreira ssncferreira changed the title fix: handle Copilot transport fallback fix: enable Copilot HTTP transport fallback Aug 26, 2026
@linear-code

linear-code Bot commented Aug 26, 2026

Copy link
Copy Markdown

@ssncferreira
ssncferreira marked this pull request as ready for review August 26, 2026 14:55
Comment thread aibridge/bridge_test.go
case agplaibridge.HostCopilotEnterprise:
return agplaibridge.ProviderCopilotEnterprise
return testProvider{name: agplaibridge.ProviderCopilotEnterprise, providerType: aibridge.ProviderCopilot}
case agplaibridge.HostChatGPT:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: Host consts are scattered between aibridgeproxyd and agplaibridge packages.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is already pre-existing to this PR. We should consolidate them into a single package, but not in the context of this PR.


// setCopilotAuth sets the Coder auth header and removes the Coder token from
// provider auth headers.
func setCopilotAuth(headers http.Header, coderToken string) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: I think setCopilotAuth should be inlined in injectBYOKHeaderIfNeeded since it is a subset of setting up BYOK. Also would look better in Server.handleRequest

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed in 49f88a8

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

What is a difference between unknown route and known route? It seems test doesn't care about route at all since it doesn't run Gateway that mounts passthough / bridged routes only.
Number of cases could be reduced to only what headers are forwarded.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This was just to test that unknonw routes (routes that are not in copilot's bridge or passthrough routes) are sent to AI Gateway with the coder token. But this is more of an AI Gateway concern, not the Proxy. I can remove this test 👍

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I don't mean remove whole test, just limit test cases to unique scenarios. Right now most test cases seem to cover the same scenario. From what I understand test should check if headers are preserved or not, id doesn't care about paths.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I meant removing the unknonw route test case. I'm adjusting the test cases to not consider the path, since that is a AI Gateway concern, not proxy

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed in 49f88a8

@ssncferreira
ssncferreira merged commit 849543d into main Aug 27, 2026
28 checks passed
@ssncferreira
ssncferreira deleted the fix/aibridge-copilot-ping-auth branch August 27, 2026 12:10
@github-actions github-actions Bot locked and limited conversation to collaborators Aug 27, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants