* feat(ai): add default_headers / resolveDefaultHeaders seam to Recipe
Generalizes per-recipe header attachment so attribution headers (OpenRouter's
HTTP-Referer + X-OpenRouter-Title) ride alongside Bearer auth on every
openai-compatible touchpoint. Two safety guards fire at applyResolveAuth time:
declaring both default_headers AND resolveDefaultHeaders throws AIConfigError
(mutual exclusion); a default header whose key shadows the resolved auth
header (Authorization, the resolver's custom header) also throws.
Reranker HTTP path at gateway.ts:2281 now merges both Authorization Bearer AND
auth.headers (where default_headers flow) into the request Headers map.
Pre-fix the ternary picked one or the other; default_headers would have been
silently dropped on the manual rerank path.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(ai): add OpenRouter provider recipe
One key, many hosted models. Configures openrouter:<provider>/<model> for
chat (GPT-5.2 family, Claude 4.5/4.6/4.7, Gemini 3 Flash Preview, DeepSeek)
and embedding (OpenAI text-embedding-3-small with Matryoshka dims_options).
max_batch_tokens=300_000 (OpenAI's aggregate per-request token cap, not the
per-input 8192 the original PR conflated).
resolveDefaultHeaders returns HTTP-Referer + X-OpenRouter-Title + X-Title
(back-compat alias) so traffic is attributed to gbrain on OR's leaderboard.
Forks override via OPENROUTER_REFERER / OPENROUTER_TITLE env vars.
supports_subagent_loop: false is informational — gbrain's subagent infra is
hard-pinned to Anthropic-direct via isAnthropicProvider() upstream regardless
of this flag. Filed as TODO to verify tool_use_id stability through OR.
Cherry-picked from PR #1210. Contributed by @davemorin.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(cli): export buildGatewayConfig + thread OPENROUTER_BASE_URL
Exports buildGatewayConfig for unit-test access. Adds one-line passthrough
for OPENROUTER_BASE_URL matching the existing LITELLM/OLLAMA/LMSTUDIO/
LLAMA_SERVER pattern so users can point at a self-hosted OR-compatible
proxy.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* test(ai): cover OpenRouter recipe + default_headers seam + wire-level headers
Four test additions:
- test/ai/recipe-openrouter.test.ts (11 cases) — recipe shape, Matryoshka
dims_options, max_batch_tokens=300K, arbitrary-ID acceptance via
assertTouchpoint, defaultResolveAuth happy/error, resolveDefaultHeaders
defaults + fork-override path, setup_hint coverage. Shape regression on
every chat/embedding model ID (catches typos without pinning the dynamic
catalog).
- test/ai/recipes-existing-regression.test.ts (+6 cases) — IRON RULE
preserved; adds default_headers contract: Bearer+defaults returns both
apiKey AND headers, custom-header+defaults merges with resolver winning,
mutual-exclusion guard, Authorization-shadow guard, custom-auth-shadow
guard, cross-touchpoint parity for all four (embedding/expansion/chat/
reranker).
- test/ai/header-transport.test.ts (3 cases) — proves headers actually reach
the wire. Synthetic recipes with resolveOpenAICompatConfig fetch wrappers
capture outgoing Headers on embed/chat/rerank. Asserts Authorization +
HTTP-Referer + X-OpenRouter-Title + X-Title all present. Codex flagged
the return-shape-only coverage gap during plan review.
- test/ai/build-gateway-config.test.ts (7 cases) — 5-way env-baseURL
passthrough sweep through the now-exported buildGatewayConfig. Uses
withEnv() from test/helpers/with-env.ts for isolation compliance. Mops
up pre-existing untested drift on LLAMA_SERVER/OLLAMA/LMSTUDIO/LITELLM
in the same pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs: add OpenRouter to embedding-providers + bump recipe count
15 -> 16 recipes. Adds OpenRouter row to the TL;DR table, a setup section
covering the value-prop (one key, many hosted models), env-var overrides
(OPENROUTER_BASE_URL, OPENROUTER_REFERER, OPENROUTER_TITLE), the subagent-
loop limitation (isAnthropicProvider() gate), and a "One key for many
hosted models" bullet under the decision tree. README updated to match.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore: bump v0.37.2.0 version refs to v0.37.4.0 across in-tree comments
v0.37.2.0 was claimed by master's takes_resolution_consistency hotfix
(#1211) before this branch could land. This commit re-stamps the source
comments that reference the OpenRouter recipe / default_headers seam to
v0.37.4.0 so the in-tree version markers match the actual landing version.
No behavior change — comments only.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore: bump version and changelog (v0.37.4.0)
One key, many hosted models — OpenRouter recipe lands. Cherry-picked from
#1210 (@davemorin), with codex review corrections folded in:
- recipe count math (16 not 17)
- current OR attribution header name (X-OpenRouter-Title, X-Title back-compat)
- max_batch_tokens semantic (300K aggregate per-request, not 8192 per-input)
- Matryoshka dims_options for text-embedding-3-small
- auth-shadow guard at applyResolveAuth
Adds the generic Recipe.default_headers / resolveDefaultHeaders seam so
attribution headers ride alongside Bearer auth. Future Together/Groq
adoption tracked in TODOS.md.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore: rebump to v0.37.6.0 (queue moved past v0.37.4/v0.37.5)
VERSION + package.json + CHANGELOG header + CLAUDE.md + TODOS.md + in-tree
source comments + llms regen. No code-behavior change.
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>