Skip to content

feat(provider): add OpenRouter as an API-key provider riding the Codex CLI#554

Open
AndreSierraM wants to merge 3 commits into
gethouston:mainfrom
AndreSierraM:feat/openrouter
Open

feat(provider): add OpenRouter as an API-key provider riding the Codex CLI#554
AndreSierraM wants to merge 3 commits into
gethouston:mainfrom
AndreSierraM:feat/openrouter

Conversation

@AndreSierraM

Copy link
Copy Markdown

What

Adds OpenRouter as a provider. It reuses the bundled codex binary pointed at OpenRouter's OpenAI-compatible endpoint with a Houston-managed API key — no parallel CLI or subscription flow.

How it's wired (scalable)

New CodexBackend on the ProviderAdapter trait: a provider that rides codex exec against a custom endpoint just describes its slug / base_url / env_key / wire_api / default_model. The codex command builder turns that into -c model_provider overrides; the runner injects the API-key env var.

Result: every codex dispatch site (session_dispatch, session_io, provider_oneshot, cli_process) treats "openai" | "openrouter" identically — the next OpenAI-compatible provider is one adapter file, no dispatch edits.

  • wire_api = "responses" (the bundled codex dropped "chat", codex#7782).
  • Adapter + HTTP-status error classifier (401/402/404/429/5xx/network → shared ProviderError taxonomy), fully unit-tested.
  • Generic key storage at <houston-home>/providers/<slug>/.env (atomic, mode 0600); set/strip in engine-core; route POST /v1/providers/openrouter/credentials.
  • Title / compaction / generate-instructions helpers wired for openrouter.

Frontend

  • providers.ts entry (loginKind: apiKey) with a curated set of cheap, capable open-source models: DeepSeek V3 (default), Llama 3.3 70B, Qwen2.5 72B, Mistral Nemo. The picker is data-driven, so it appears automatically.
  • Generic ApiKeyConnectDialog (replaces the gemini-specific dialog in picker + settings) + generic setProviderApiKey client method.
  • Official OpenRouter logo; en/es/pt strings.

Tests

cargo test green across terminal-manager / engine-core / engine-server (incl. new classifier, codex override, credential, and providers-route tests). pnpm tsc --noEmit + pnpm check-locales green.

Docs

agent-manifest, provider-errors, engine-protocol, auth updated.

🤖 Generated with Claude Code

AndreSierraM and others added 3 commits June 17, 2026 12:09
…x CLI

OpenRouter connects through the existing bundled codex binary pointed at
OpenRouter's OpenAI-compatible endpoint with a Houston-managed API key — no
parallel CLI or subscription flow.

Engine
- New `CodexBackend` on the `ProviderAdapter` trait: a provider that rides
  `codex exec` against a custom endpoint describes its slug / base_url /
  env_key / wire_api / default_model. The codex command builder turns it into
  `-c model_provider` overrides; the runner injects the API-key env var. So
  every codex dispatch site (session_dispatch, session_io, provider_oneshot,
  cli_process) treats "openai" | "openrouter" identically and the next
  OpenAI-compatible provider is one adapter file — no dispatch edits.
- `wire_api = "responses"` (the bundled codex dropped "chat", codex#7782).
- Adapter + HTTP-status error classifier (401/402/404/429/5xx/network →
  shared ProviderError taxonomy, fully unit-tested).
- Generic key storage at `<houston-home>/providers/<slug>/.env` (atomic,
  0600); set/strip via engine-core; route `/providers/openrouter/credentials`.
- Title / compaction / generate helpers wired for openrouter.

Frontend
- `providers.ts` entry (loginKind: apiKey) with a curated set of cheap, capable
  open-source models (DeepSeek V3 default, Llama 3.3 70B, Qwen2.5 72B, Mistral
  Nemo). Picker is data-driven, so it appears automatically.
- Generic `ApiKeyConnectDialog` (replaces the gemini-specific dialog in the
  picker + settings) + generic `setProviderApiKey` client method.
- Official OpenRouter logo, en/es/pt strings.

Docs: agent-manifest, provider-errors, engine-protocol, auth.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ifier

OpenRouter sessions complete fine, but codex prints two non-fatal diagnostics
on stderr that were producing false provider-error cards:

- `codex_models_manager` logs a failed catalog refresh (OpenRouter returns
  `{"data":[...]}`, codex expects `{"models":[...]}`) and dumps the entire
  multi-hundred-KB catalog body. That body almost always contains a bare
  "401" (pricing/ids), which `is_auth_error`'s substring check turned into a
  false "Reconnect to OpenRouter" Unauthenticated card on an otherwise
  successful turn.
- `rmcp::` logs auth failures for MCP servers configured in the user's own
  `~/.codex/config.toml` — the user's MCP, not the OpenRouter turn.

`classify_stderr` now early-returns `None` for these codex-internal lines.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two speed/correctness improvements for OpenRouter (both ride the same codex
backend mechanism, so they're surgical):

- **Isolated CODEX_HOME.** OpenRouter sessions now run codex against
  `<houston-home>/codex-home` instead of the user's `~/.codex`, so codex never
  loads the user's personal `~/.codex/config.toml` (their own MCP servers +
  settings) into a Houston session. This removes a per-spawn MCP connection
  attempt (the user's MCP failing auth was leaking a stray reconnect card) and
  cut a real session's input from ~23k to ~13k tokens — the user's MCP
  tool-defs were being injected into context every turn. Native codex (OpenAI)
  keeps `~/.codex` (it needs the OAuth `auth.json` there). `codex_rollout` now
  searches both homes for token-usage, so the context gauge keeps working.

- **No reasoning effort.** The curated OpenRouter models are non-reasoning chat
  models, so `model_reasoning_effort` is dead overhead; the adapter now reports
  no effort levels and the runner omits the flag.

Skipped `:nitro` throughput routing on purpose — it routes to premium providers
and would undercut the "cheap open-source" model curation.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant