Skip to content

Feat/openrouter - #239

Merged
robdmac merged 6 commits into
mainfrom
feat/openrouter
Jun 3, 2026
Merged

robdmac merged 6 commits into
mainfrom
feat/openrouter

Conversation

@robdmac

@robdmac robdmac commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

No description provided.

robdmac and others added 6 commits May 21, 2026 08:37
End-to-end OpenRouter integration with a per-terminal Model picker in the
terminal settings dropdown. Picking a non-default model sets the existing
"restart to apply" banner so configuration changes can be batched.

Supported harnesses:
  - Codex / OpenCode / Droid via OpenAI-compatible endpoint (broker
    "openrouter" provider, target https://openrouter.ai/api/v1)
  - Claude Code via Anthropic-compatible endpoint (sibling broker entry
    "openrouter-anthropic", target https://openrouter.ai/api so the SDK's
    /v1/messages append produces the correct final URL). The broker rewrites
    the auth header to Bearer <OPENROUTER_API_KEY>; the OpenRouter model id
    is written into .claude/settings.local.json. Switching back to the
    default Anthropic provider clears the provider-prefixed id.
  - Gemini CLI: not supported (Google-specific protocol).

Plumbing:
  - frontend: openrouter-models.json catalog with per-model pricing /
    context / compatibleHarnesses; Model panel in TerminalBlock; modelSelection
    persisted through terminalContent JSON like bootCommand/ttsProvider.
  - control plane: parseTerminalConfig surfaces modelSelection;
    sandbox.createPty forwards it as model_selection in the request body.
  - sandbox: CreatePTYWithOptions is the new canonical entry point;
    applyOpenRouterEnv injects per-harness env vars (OPENAI_BASE_URL or
    ANTHROPIC_BASE_URL pointing at the local broker URL). One OPENROUTER_API_KEY
    secret installs broker configs for every sibling provider via
    broker.GetAllProvidersByEnvKey.
  - egress allowlist: openrouter.ai + *.openrouter.ai under llm_api.

Catalog freshness:
  - frontend/scripts/check-catalogs.mjs diffs openrouter-models.json and
    claude-subagents.json against upstream sources; wired to
    `npm run check-catalogs` and a weekly GitHub Action that files an
    issue on drift.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Fixes #1-#7 from the code review plus markdown docs:
- Null-check pricing/context in the Model panel — bad catalog entries no
  longer crash the render.
- Delete CreatePTYWithToken — zero callers after the CreatePTYWithOptions
  refactor; was unused indirection.
- Use broker.GetDummyValue("anthropic") for the OpenRouter-Claude
  ANTHROPIC_API_KEY placeholder so LLM-visible env values use the same
  [BROKERED] wording as every other provider.
- Empty BrokerEnvKey on the openrouter-anthropic sibling spec — env.go
  only reads the primary's BrokerEnvKey, so the field was dead.
- Replace "init" literal with time.Now().Format(time.RFC3339) in
  model_selection.go's init log, matching the project convention.
- Drop brokerHostFromEnv() (read an env var that was set nowhere) and
  thread Session.BrokerPort() into applyOpenRouterEnv directly.
- Drop OPENROUTER_BASE_URL / OPENROUTER_MODEL env writes — no known
  consumer in this codebase.
- Docs: add Model Selection / OpenRouter notes to CLAUDE.md (root),
  frontend/CLAUDE.md, and sandbox/CLAUDE.md (broker providers list +
  egress allowlist enumeration).

Punted to a follow-up: extract buildTerminalContent helper for the
10-site JSON.stringify pattern, unify .claude/settings.local.json
mutators behind one helper, tighten ModelSelection to a discriminated
union, share provider-string constants across layers, flatten the
Claude branch in session.go, extract a shared ConfigPanel component.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… codes

Addresses code-review findings on the OpenRouter integration:

- model_selection.go: set a dummy OPENAI_API_KEY when routing Codex/OpenCode/
  Droid through OpenRouter. OpenAI-compatible clients refuse to construct/send
  a request with no API key, so the request never reached the broker and the
  feature was silently broken for those harnesses (3 of the 4 supported, and
  the only harnesses for the 9 non-Anthropic catalog models). The broker strips
  the placeholder and injects the real OpenRouter Bearer token — mirrors the
  ANTHROPIC_API_KEY placeholder and the dummy the default Codex flow sets.

- session.go: make CreatePTY a thin wrapper over CreatePTYWithOptions instead
  of a ~250-line duplicate. CreatePTYWithOptions is a strict superset, so the
  no-opts path is behaviour-equivalent and now also clears a stale OpenRouter
  model id from .claude/settings.local.json. Removes the duplication that let
  the two paths drift (and matches the doc comment that already called it a
  "thin wrapper").

- check-catalogs.yml: capture the script's real exit code so drift (1) is
  distinguished from an upstream/network error (2). A transient fetch blip no
  longer opens a spurious "drift" issue or evaluates drift; it emits a warning
  annotation instead. The job still fails and files an issue only on real drift.

Verified: go build ./..., go vet, and go test ./internal/sessions/... all pass;
workflow YAML parses.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The catalog freshness check (check-catalogs.yml → "Fail job if drift") was
failing on real drift against the live OpenRouter API:

- google/gemini-2.0-flash-001 was removed/renamed upstream → replace with its
  current successor google/gemini-2.5-flash ($0.30/$2.50 per 1M, 1M context).
- deepseek/deepseek-chat price moved $0.32/$0.89 → $0.20/$0.80.

Bumped lastUpdated. `npm run check-catalogs` now reports all catalogs current.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@robdmac
robdmac merged commit 619ab13 into main Jun 3, 2026
2 checks passed
@robdmac
robdmac deleted the feat/openrouter branch June 3, 2026 18:06
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