Skip to content

feat(wrap): add OpenCode support with headroom wrap opencode#1173

Open
praffulomg wants to merge 1 commit into
chopratejas:mainfrom
praffulomg:feat/wrap-opencode
Open

feat(wrap): add OpenCode support with headroom wrap opencode#1173
praffulomg wants to merge 1 commit into
chopratejas:mainfrom
praffulomg:feat/wrap-opencode

Conversation

@praffulomg

@praffulomg praffulomg commented Jun 19, 2026

Copy link
Copy Markdown

Description

Adds headroom wrap opencode, extending Headroom's existing wrap family (claude, codex, copilot, aider, cursor, ...) to the OpenCode CLI so its LLM traffic is routed through Headroom's local compression proxy.

OpenCode does not honor the generic OPENAI_BASE_URL / ANTHROPIC_BASE_URL environment variables (it only uses env vars for API keys and ${VAR} substitution). Endpoint redirection must therefore be injected via OpenCode's own config mechanism. The wrapper sets the OPENCODE_CONFIG_CONTENT env var to an inline JSON config that points both the openai and anthropic providers' baseURL at the local proxy.

Both baseURLs are /v1-based (http://127.0.0.1:{port}/v1). This is deliberate and load-bearing: @ai-sdk/openai appends /chat/completions and @ai-sdk/anthropic appends only /messages to the configured base, so a /v1 base resolves to the proxy's existing /v1/chat/completions and /v1/messages routes. A base without /v1 would silently fall through the proxy's verbatim catch-all and forward uncompressed with no error.

Type of Change

  • New feature (non-breaking change which adds functionality)
  • Bug fix
  • Breaking change
  • Documentation
  • Performance
  • Refactor

Changes Made

  • New package headroom/providers/opencode/
    • __init__.py — re-exports build_launch_env and proxy_base_url (mirrors providers/codex).
    • runtime.pyproxy_base_url(port) -> http://127.0.0.1:{port}/v1; build_launch_env(port, environ, project) emits OPENCODE_CONFIG_CONTENT JSON with both providers' baseURL set to the /v1 proxy base, an optional X-Headroom-Project header (via sanitize_project_name) for per-project savings attribution, and autoupdate: false to pin OpenCode under the wrapper.
  • headroom/cli/wrap.py — added the _build_opencode_launch_env import and a new @wrap.command("opencode") (mirrors the additive Pattern A launch flow: resolve binary via shutil.which("opencode") → build env → _launch_tool(..., tool_label="OPENCODE", agent_type="opencode")); updated the wrap group docstring; removed an obsolete note stating the command did not exist.
  • headroom/telemetry/context.py — added "opencode" to _KNOWN_WRAP_AGENTS (telemetry stack label only).
  • README.md — added an OpenCode row to the agent compatibility matrix and the wrap synopsis.
  • CHANGELOG.md — added an Unreleased > Features entry.
  • tests/test_provider_opencode.py — new unit tests (5 assertions).

Additive only. No changes to the proxy, provider registry, or auth-mode classification — existing wraps are unaffected.

Testing

  • Unit tests pass (pytest)
  • Linting passes (ruff check)
  • Type checking passes (mypy on the new package)
  • New tests added
  • Manual testing performed

Test Output

$ uv run --no-sync pytest tests/test_provider_opencode.py -q
5 passed in 0.08s

# Full provider + telemetry + test_providers/ regression (implementation pass):
231 passed, 0 failed, 0 skipped

$ uv run --no-sync ruff check headroom/cli/wrap.py headroom/telemetry/context.py headroom/providers/opencode/ tests/test_provider_opencode.py
All checks passed!

$ uv run --no-sync mypy headroom/providers/opencode/
Success: no issues found in 2 source files

$ uv run --no-sync python -c "import headroom.cli.wrap; print('wrap import OK')"
wrap import OK

Real Behavior Proof

  • Environment: macOS (darwin), Python 3.11.15, pytest 9.0.3, repo chopratejas/headroom @ branch feat/wrap-opencode (commit 6b844ce). opencode binary NOT installed in this sandbox.
  • Exact command / steps: uv run --no-sync ruff check headroom/cli/wrap.py headroom/telemetry/context.py headroom/providers/opencode/ tests/test_provider_opencode.py; uv run --no-sync mypy headroom/providers/opencode/; uv run --no-sync pytest tests/test_provider_opencode.py -q; uv run --no-sync python -c "from headroom.providers.opencode import build_launch_env, proxy_base_url; print(build_launch_env(8787, {}, 'Demo Proj'))"; and Click CliRunner invocations of wrap --help and wrap opencode --help.
  • Observed result: both openai+anthropic provider baseURLs = http://127.0.0.1:8787/v1 (both end /v1); X-Headroom-Project header present when project set and absent when project=None; headroom wrap --help lists opencode with claude/codex/aider/copilot/cursor still intact; opencode subcommand --help exits 0; ruff "All checks passed!"; mypy "Success: no issues found in 2 source files"; pytest 5 passed (new) + 231 passed regression (0 failed/skipped).
  • Not tested: live end-to-end run against a real installed opencode binary hitting a real upstream API (binary not available in sandbox); mypy was run only on the new headroom/providers/opencode package, not the full headroom tree.

Review Readiness

  • I have performed a self-review
  • This PR is ready for human review

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code where necessary (the critical /v1 rationale is documented in runtime.py)
  • I have made corresponding changes to the documentation (README + CHANGELOG)
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective / feature works
  • New and existing unit tests pass locally with my changes
  • I have updated the CHANGELOG (Unreleased > Features)

Additional Notes

  • The /v1 suffix on both provider baseURLs is intentional and the most important correctness detail — tests/test_provider_opencode.py explicitly asserts both end in /v1 to guard against a silent regression (a wrong path falls through the proxy catch-all uncompressed with no 404 in logs).
  • Per-project savings attribution uses the X-Headroom-Project header (OpenCode supports custom per-provider headers via config), consistent with the proxy's existing header-based classify_project.
  • Scope is intentionally minimal: no headroom install opencode, no Docker/scripts changes, and no auth-mode/UA classification (OpenCode is BYOK → PAYG, which is the correct aggressive-compression mode; classify_client already maps the opencode/ user agent). These are noted as possible follow-ups.
  • mypy was run against the new headroom/providers/opencode/ package (clean), not the entire headroom tree.

OpenCode (anomalyco/opencode) is an OpenAI/Anthropic-compatible client that does not honor OPENAI_BASE_URL/ANTHROPIC_BASE_URL env vars. The new wrap target injects an inline OPENCODE_CONFIG_CONTENT config pointing both the openai and anthropic providers baseURL at the local Headroom proxy. Both baseURLs are /v1-based so @ai-sdk/openai resolves to /v1/chat/completions and @ai-sdk/anthropic (which appends only /messages) resolves to /v1/messages -- the proxy actual routes. Per-project savings are attributed via the X-Headroom-Project header; autoupdate is disabled to pin OpenCode under the wrapper.

Additive only: new headroom/providers/opencode/ package + @wrap.command("opencode") mirroring the aider/vibe pattern. No proxy/registry/auth-mode changes (OpenCode is BYOK = PAYG, already mapped). Existing wraps unaffected.
@github-actions

github-actions Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

PR governance

This PR follows the template and is marked ready for human review.

@github-actions github-actions Bot added status: needs author action Pull request body or readiness checklist still needs author updates status: ready for review Pull request body is complete and the author marked it ready for human review and removed status: needs author action Pull request body or readiness checklist still needs author updates labels Jun 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

status: ready for review Pull request body is complete and the author marked it ready for human review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant