Skip to content

[BUG] OpenCode plugin never sends x-headroom-project — Per-Project Savings dashboard can't attribute OpenCode traffic #2847

Description

@artsaraiva

Summary

The OpenCode transport plugin (headroom/providers/opencode/_dist/entry.opencode.js) sets HEADROOM_PROJECT as a spawned-subprocess env var only (shell.env hook, derived from input.project.id), but the fetch/http/https wrapping installed by installHeadroomTransport only ever sets x-headroom-base-url / x-headroom-original-path headers on routed requests. It never sets x-headroom-project.

Server-side, headroom/proxy/project_policy.py (classify_project) only attributes savings via the x-headroom-project header or a /p/<name> URL-path prefix. Since the OpenCode plugin never sends either on the actual proxied HTTP requests, every OpenCode request routed through Headroom is unattributed — the dashboard's "Per-Project Savings" panel permanently shows 0 project(s) for OpenCode users, even with --memory --memory-storage project running correctly (that's a separate, cwd-based attribution path used only for the memory DB, not for cost/savings attribution).

Environment

  • headroom-ai 0.34.0
  • OpenCode (current, plugin loaded via packaged _dist/entry.opencode.js)
  • macOS arm64

Reproduction

  1. Run headroom proxy --memory --memory-storage project (or any profile with the OpenCode plugin active).
  2. Use OpenCode with the Headroom transport plugin loaded (OPENCODE_CONFIG_CONTENTplugin: [...entry.opencode.js]).
  3. Make several requests through the proxy.
  4. Open the Headroom dashboard → "Per-Project Savings" panel stays at 0 project(s) / "No per-project data yet." regardless of traffic volume.

Expected

OpenCode traffic routed through the proxy should be attributable per-project, same as Claude Code (/p/<name> base URL) or any client sending x-headroom-project.

Suggested fix

Resolve input.project.id (falling back to pluginOptions.project / input.directory) once at plugin init in HeadroomPlugin, thread it through installHeadroomTransport's state, and set an x-headroom-project header alongside the existing x-headroom-base-url header in both header-construction seams:

  • mergeFetchHeaders (used by the wrapped fetch)
  • headersForNodeRequest (used by the wrapped http.request/https.request)

I have a working patch against the packaged _dist/entry.opencode.js (adds a project parameter threaded through installHeadroomTransportwrapRequest/routedNodeOptionsheadersForNodeRequest, and through the fetch wrapper → withRoutedFetchInputmergeFetchHeaders) and can share a diff/PR if useful.

Related but distinct issues

Metadata

Metadata

Assignees

No one assigned

    Labels

    LowMinor bugs and issues that do not prevent usability

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions