From b4f9b618d3bfff83db1b92c9fe4856632f8eb569 Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Wed, 22 Apr 2026 13:25:45 +0200 Subject: [PATCH] fix(coding-agent): allow headers-only model overrides closes #3538 --- packages/coding-agent/CHANGELOG.md | 1 + .../coding-agent/src/core/model-registry.ts | 6 ++--- .../coding-agent/test/model-registry.test.ts | 22 +++++++++++++++++++ 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 37374a77..26e7e86e 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -13,6 +13,7 @@ ### Fixed - Fixed extension session-replacement flows so `ctx.newSession()`, `ctx.fork()`, `ctx.switchSession()`, and imported-session replacements fully rebind before post-switch work runs, added `withSession` replacement callbacks with fresh `ReplacedSessionContext` helpers, and make stale pre-replacement `pi` / `ctx` session-bound accesses throw instead of silently targeting the wrong session ([#2860](https://github.com/badlogic/pi-mono/issues/2860)) +- Fixed `models.json` built-in provider overrides to accept `headers` without requiring `baseUrl`, so request-header-only overrides now load and apply correctly ([#3538](https://github.com/badlogic/pi-mono/issues/3538)) ## [0.68.1] - 2026-04-22 diff --git a/packages/coding-agent/src/core/model-registry.ts b/packages/coding-agent/src/core/model-registry.ts index fc05c1c3..fbfac4b3 100644 --- a/packages/coding-agent/src/core/model-registry.ts +++ b/packages/coding-agent/src/core/model-registry.ts @@ -474,10 +474,10 @@ export class ModelRegistry { providerConfig.modelOverrides && Object.keys(providerConfig.modelOverrides).length > 0; if (models.length === 0) { - // Override-only config: needs baseUrl, compat, modelOverrides, or some combination. - if (!providerConfig.baseUrl && !providerConfig.compat && !hasModelOverrides) { + // Override-only config: needs baseUrl, headers, compat, modelOverrides, or some combination. + if (!providerConfig.baseUrl && !providerConfig.headers && !providerConfig.compat && !hasModelOverrides) { throw new Error( - `Provider ${providerName}: must specify "baseUrl", "compat", "modelOverrides", or "models".`, + `Provider ${providerName}: must specify "baseUrl", "headers", "compat", "modelOverrides", or "models".`, ); } } else if (!isBuiltIn) { diff --git a/packages/coding-agent/test/model-registry.test.ts b/packages/coding-agent/test/model-registry.test.ts index 470ef926..4a0ffc15 100644 --- a/packages/coding-agent/test/model-registry.test.ts +++ b/packages/coding-agent/test/model-registry.test.ts @@ -135,6 +135,28 @@ describe("ModelRegistry", () => { } }); + test("headers-only override resolves at request time", async () => { + writeRawModelsJson({ + anthropic: { + headers: { + "X-Custom-Header": "custom-value", + }, + }, + }); + + const registry = ModelRegistry.create(authStorage, modelsJsonPath); + expect(registry.getError()).toBeUndefined(); + const anthropicModels = getModelsForProvider(registry, "anthropic"); + + for (const model of anthropicModels) { + const auth = await registry.getApiKeyAndHeaders(model); + expect(auth.ok).toBe(true); + if (auth.ok) { + expect(auth.headers?.["X-Custom-Header"]).toBe("custom-value"); + } + } + }); + test("baseUrl-only override does not affect other providers", () => { writeRawModelsJson({ anthropic: overrideConfig("https://my-proxy.example.com/v1"),