headroom/tests/test_cli
Matt Van Horn 1db6d88ab4
fix(wrap): honor Copilot OAuth wire-api override and model default (#2387)
## Description

`headroom wrap copilot -- --model gpt-5.4` fails with `400 The requested
model is not available for integrator "copilot-language-server"`, even
though the same GitHub account uses GPT-5.4 fine in the native `copilot`
CLI. On the hosted Copilot OAuth path (authenticated via `headroom
copilot-auth`, no `--subscription`), `headroom/cli/wrap.py` forced the
`completions` wire API for every non-`--subscription` launch and ignored
a caller-supplied `COPILOT_PROVIDER_WIRE_API`. GPT-5.x / o-series
reasoning models need the `responses` wire API.

The OAuth path now honors a valid inherited `COPILOT_PROVIDER_WIRE_API`
(`completions` or `responses`) and otherwise uses the model-aware
default via `_copilot_default_wire_api_for_model(selected_model)`, so
GPT-5.x routes to `responses` while GPT-4.1 stays on `completions`. The
`--subscription` path is unchanged.

This supersedes the earlier closed #2243, which mixed the fix with
unrelated proxy/CI changes and hit merge conflicts. This PR ships only
the `headroom/cli/wrap.py` wire-api selection plus regression tests,
rebased clean on `main`.

Closes #2222

## Type of Change

- [x] Bug fix (non-breaking change that fixes an issue)
- [ ] New feature (non-breaking change that adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] Documentation update
- [ ] Performance improvement
- [ ] Code refactoring (no functional changes)

## Changes Made

- Hosted Copilot OAuth launch resolves the wire API from an explicit
`COPILOT_PROVIDER_WIRE_API` env value when it is
`completions`/`responses`, else from
`_copilot_default_wire_api_for_model(selected_model)` instead of a
hardcoded `completions`. The `subscription`-only gating on the
model-aware default is removed so OAuth and subscription paths pick the
same model-correct wire API.

## Testing

- [x] Unit tests pass (`pytest`)
- [ ] Linting passes (`ruff check .`)
- [ ] Type checking passes (`mypy headroom`)
- [x] New tests added for new functionality
- [ ] Manual testing performed

### Test Output

```text
$ .venv/bin/python -m pytest tests/test_cli/test_wrap_copilot.py tests/test_provider_copilot_wrap.py -q
70 passed in 0.28s
```

New tests cover: the OAuth path honoring an inherited
`COPILOT_PROVIDER_WIRE_API`, the OAuth path resolving GPT-5.4 to
`responses`, and `default_wire_api_for_model("gpt-5.4")` returning
`responses`.

## Real Behavior Proof

- Environment: local checkout, Python 3.14, target tests only.
- Exact command / steps: `.venv/bin/python -m pytest
tests/test_cli/test_wrap_copilot.py tests/test_provider_copilot_wrap.py
-q`
- Observed result: 70 passed; the OAuth-path tests assert
`env["COPILOT_PROVIDER_WIRE_API"] == "responses"` for GPT-5.x and honor
an inherited override.
- Not tested: the end-to-end live Copilot `400` reproduction, which
needs a real Copilot OAuth session plus a GPT-5.x request. The wire-api
selection that caused the `400` is covered by the unit tests above.

## Review Readiness

- [x] I have performed a self-review
- [x] This PR is ready for human review

## Checklist

- [x] My code follows the project's style guidelines
- [x] I have performed a self-review of my code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] New and existing unit tests pass locally with my changes
- [x] I did **not** edit `CHANGELOG.md` — it is generated by
release-please from my Conventional Commit PR title (a CI guard enforces
this)

## Screenshots (if applicable)

N/A — CLI behavior change covered by the unit tests above.

## Additional Notes

The change is scoped to the wire-api selection line; the
`--subscription` path and the existing explicit `--wire-api` CLI flag
are untouched.

AI was used for assistance.

---------

Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
2026-08-12 00:04:42 -05:00
..
conftest.py fix: remove rtk and lean-ctx CLI context tools (#2677) 2026-07-30 22:59:41 -07:00
test_copilot_auth.py fix(auth): support GitHub Enterprise Copilot OAuth domain (#2192) 2026-07-14 16:07:17 -04:00
test_init_cli.py fix(cli/init): fail clearly on a target settings file with invalid JSON (#2227) 2026-07-15 18:15:45 +00:00
test_init_enable_tool_search.py fix(init): set ENABLE_TOOL_SEARCH=true so Claude Code keeps deferring tools (#746) (#995) 2026-06-19 11:26:26 -05:00
test_install_cli.py fix(install): carry upstream-routing env overrides into supervised deployments (#2429) 2026-07-19 22:18:15 -07:00
test_main_help_version.py fix: remove rtk and lean-ctx CLI context tools (#2677) 2026-07-30 22:59:41 -07:00
test_mcp.py feat(mcp): add streamable HTTP MCP transport (#1773) 2026-07-14 13:25:45 -04:00
test_mcp_status.py fix(mcp): mcp status checks ~/.claude.json, not only ~/.claude/mcp.json (#990) 2026-07-15 09:25:52 -05:00
test_recover_codex.py test(recover-codex): bind AF_UNIX socket via short relative path (#2396) 2026-08-02 13:14:31 -07:00
test_serena_disable.py fix(serena): stop the Serena dashboard popup and make --no-serena actually disable Serena (#1003) 2026-06-14 23:32:46 -07:00
test_serena_migrate.py fix(wrap/serena): stop creating serena_config.yml, unbricking Serena on fresh installs (#2676) 2026-07-30 20:55:47 -07:00
test_subprocess_utf8_encoding.py fix(windows): pin UTF-8 encoding on text-mode subprocess calls (#1311) 2026-06-23 12:52:49 -05:00
test_unwrap_claude.py fix: remove rtk and lean-ctx CLI context tools (#2677) 2026-07-30 22:59:41 -07:00
test_wrap_aider.py fix: remove rtk and lean-ctx CLI context tools (#2677) 2026-07-30 22:59:41 -07:00
test_wrap_bridge.py fix: remove rtk and lean-ctx CLI context tools (#2677) 2026-07-30 22:59:41 -07:00
test_wrap_claude_base_url.py fix: remove rtk and lean-ctx CLI context tools (#2677) 2026-07-30 22:59:41 -07:00
test_wrap_claude_finally_unbound.py fix: remove rtk and lean-ctx CLI context tools (#2677) 2026-07-30 22:59:41 -07:00
test_wrap_claude_vertex_proxy_env.py fix(wrap/claude): keep --1m effective when an explicit --model is passed through 2026-08-11 18:15:40 -07:00
test_wrap_codex.py fix: remove rtk and lean-ctx CLI context tools (#2677) 2026-07-30 22:59:41 -07:00
test_wrap_copilot.py fix(wrap): honor Copilot OAuth wire-api override and model default (#2387) 2026-08-12 00:04:42 -05:00
test_wrap_dead_marker_selfheal.py fix(wrap): self-heal a stale ANTHROPIC_BASE_URL left by a dead proxy (#2223) 2026-07-15 19:58:10 +00:00
test_wrap_encoding.py fix: remove rtk and lean-ctx CLI context tools (#2677) 2026-07-30 22:59:41 -07:00
test_wrap_goose.py fix: remove rtk and lean-ctx CLI context tools (#2677) 2026-07-30 22:59:41 -07:00
test_wrap_grok.py fix: remove rtk and lean-ctx CLI context tools (#2677) 2026-07-30 22:59:41 -07:00
test_wrap_helpers.py fix(wrap/claude): keep --1m effective when an explicit --model is passed through 2026-08-11 18:15:40 -07:00
test_wrap_kimi.py feat(wrap): add headroom wrap kimi for Kimi CLI (#1426) 2026-07-15 21:40:58 +00:00
test_wrap_omp.py fix: remove rtk and lean-ctx CLI context tools (#2677) 2026-07-30 22:59:41 -07:00
test_wrap_openclaude.py fix: remove rtk and lean-ctx CLI context tools (#2677) 2026-07-30 22:59:41 -07:00
test_wrap_openclaw.py fix(wrap): use canonical headroom-openclaw npm package for wrap openclaw (#1969) (#2120) 2026-07-13 16:33:44 -04:00
test_wrap_opencode.py fix(wrap/opencode): verify the opencode binary before mutating config 2026-08-11 18:15:37 -07:00
test_wrap_openhands.py fix: remove rtk and lean-ctx CLI context tools (#2677) 2026-07-30 22:59:41 -07:00
test_wrap_persistent.py feat(opencode): support Copilot subscription backend for headroom models (#2441) (#2445) 2026-07-20 11:02:14 -07:00
test_wrap_proxy_detach.py fix(wrap): detach the shared proxy on Windows so it survives an ungraceful agent close (#1464) 2026-06-30 13:49:28 -05:00
test_wrap_serena_boost.py fix(wrap/serena): install Serena from the serena-agent PyPI wheel, not the git source 2026-08-11 09:10:32 -07:00
test_wrap_stale_marker.py fix: detect and clear stale ANTHROPIC_BASE_URL from crashed wrap sessions (#1768) (#1837) 2026-07-06 08:35:40 -07:00
test_wrap_vibe.py fix: remove rtk and lean-ctx CLI context tools (#2677) 2026-07-30 22:59:41 -07:00
test_wrap_vscode.py feat(copilot): proxy VS Code models transparently (#2687) 2026-08-03 04:42:48 -07:00
test_wrap_vscode_claude.py feat(claude): support Claude Code in VS Code (#2752) 2026-08-03 20:14:13 -07:00
test_wrap_zcode.py fix: remove rtk and lean-ctx CLI context tools (#2677) 2026-07-30 22:59:41 -07:00