Commit graph

1 commit

Author SHA1 Message Date
Rod Boev
232fb49c73
fix(proxy): route Codex Live voice through a dedicated /v1/live transport (#2709)
## Description

Codex Live traffic currently reaches an unrouted WebSocket path and
receives HTTP 403 before the proxy can contact an upstream.

Closes #2653

## Type of Change

- [x] Bug fix (non-breaking change that fixes an issue)
- [ ] New feature
- [ ] Breaking change
- [ ] Documentation update
- [ ] Performance improvement
- [ ] Code refactoring

## Changes Made

- Add a dedicated `/v1/live` WebSocket route family and transparent
transport.
- Preserve subscription auth routing, account headers, origin policy,
subprotocols, and text/binary frame bytes.
- Propagate WebSocket close metadata and cancel relay tasks
deterministically on every exit.
- Keep Live outside the Responses parser, compression, memory injection,
and Responses beta-header path.
- Keep generic HTTP paths on the existing catch-all and document the
Live aliases plus the derived-path override.
- Add real-app route, relay, and loopback integration proof.
- Add coverage for authorization fallback, defensive receive events, and
cancellation cleanup in the Live relay.

## Testing

The focused Live handshake, preservation suites, Ruff, format, and diff
checks pass. The base comparison, Codex Desktop owner round trip, and
ChatGPT backend acceptance of the derived `/backend-api/codex/live` path
remain untested.

- [x] Unit tests pass (`pytest`)
- [x] Linting passes (`ruff check`)
- [x] Type checking passes (`mypy headroom --ignore-missing-imports`)
- [x] New tests added for the reported failure
- [x] Manual loopback testing performed

### Test Output

```text
uv run pytest tests/test_codex_live.py -q: 6 passed, 7 warnings in 11.03s
uv run pytest tests/test_provider_proxy_routes.py tests/test_proxy_codex_route_aliases.py tests/test_provider_codex_endpoints.py tests/test_openai_codex_routing.py -q: 53 passed in 90.54s (0:01:30)
uv run ruff check headroom/providers/codex/live.py headroom/providers/proxy_routes.py headroom/proxy/handlers/openai.py headroom/proxy/ws_headers.py tests/test_codex_live.py: All checks passed
uv run ruff format --check headroom/providers/codex/live.py headroom/providers/proxy_routes.py headroom/proxy/handlers/openai.py headroom/proxy/ws_headers.py tests/test_codex_live.py: 5 files already formatted
uv run mypy headroom --ignore-missing-imports: Success: no issues found in 508 source files
git diff --check: pass
```

## Real Behavior Proof

The local WebSocket integration floor uses real uvicorn, a real
WebSocket client, and a real loopback WebSocket upstream. The base 403
comparison was not run. Head observes HTTP 101 on every Live alias and a
byte-identical binary frame relay.

- Environment: Windows, CPython 3.13, the Headroom proxy test
environment.
- Exact command / steps: run the focused Live test against the local
uvicorn proxy and loopback WebSocket upstream, then run the preservation
suite listed in `Test Output`.
- Observed result: all four Live aliases return HTTP 101, negotiate
`codex.live.v1`, preserve text and binary frames, and pass the
preservation suite.
- Not tested: Codex Desktop Live session; ChatGPT backend acceptance of
`/backend-api/codex/live`; the base 403 comparison.

## Review Readiness

- Live has a separate transport and does not enter Responses handling.
- Existing Responses and generic passthrough suites remain preservation
gates.
- No `CHANGELOG.md` or install/crate changes are included.
- [x] I have performed a self-review
- [x] This PR is ready for human review

## Checklist

- [x] Closes #2653
- [x] Real loopback handshake and binary-frame proof required
- [x] No audio payload logging
- [x] No unqualified end-to-end claim

## Screenshots

Not applicable.

## Additional Notes

The upstream Live path is derived from the repository’s Codex URL
formula and remains explicitly unconfirmed until owner evidence is
available.
2026-08-02 13:15:47 -07:00