Skip to content

fix(core): don't enable catalog providers from setting-only config overrides - #59440

Open
opencode-agent[bot] wants to merge 1 commit into
devfrom
core-config-override-activation
Open

opencode-agent[bot] wants to merge 1 commit into
devfrom
core-config-override-activation

Conversation

@opencode-agent

@opencode-agent opencode-agent Bot commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Problem

The config provider plugin set activation = "enabled" on every provider entry in user and project config. Provider.snapshot treats enabled as available whether or not there is a connection. So an entry that only tunes a catalog provider, for example

{ "provider": { "openai": { "settings": { "timeout": 60000 } } } }

made openai and its whole catalog show up as available with no key, even though requests still need one.

Rule

overlayProvider now only enables a provider when one of these is true:

  • It is new. No provider with that ID exists before the config overlay in the provider fold. A custom provider declared in config with its models is still available right away with no connection.
  • It comes from an integration document (integrate(...)). This is unchanged: these providers stay enabled and bound to the integration's connection through sourceConnection.
  • The config entry has an inline credential. That means a non-empty string settings.apiKey, settings.authToken or settings.accessToken, the same keys the model resolver treats as configured auth. These providers can authenticate, so they stay enabled. This matches the behavior before activation was introduced, when a string settings.apiKey made a provider available.

In every other case the existing provider keeps its activation (for example auto from models.dev, or enabled from a local discovery plugin). It still needs a real connection.

Edge cases:

  • canonical copies under a new ID ({"my-openai": {"canonical": "openai"}}) count as new providers and are enabled, as before.
  • Declaring models on an existing provider does not enable it. Aliases on openai still need an openai connection.
  • A later config document overriding a provider an earlier document created keeps it enabled, because the earlier overlay already enabled it in the same fold.
  • Empty credential strings do not count as a credential.
  • Config schema: it has no activation field, so there is nothing to honor there. No schema or public API changes.

Tests

  • New: a timeout-only override of a catalog provider stays auto, is unavailable without a connection, and becomes available once a key credential exists. A custom config provider and a canonical copy are available with no connection.
  • New: an inline apiKey on a catalog provider enables it; an empty apiKey does not.
  • Existing config provider tests that override a pre-seeded provider and read its models now mark that fixture as enabled. They test compatibility, capabilities and variants, not availability.
  • bun test test/config/provider.test.ts: 41 pass.
  • The full packages/core suite only fails in host-environment tests (ShellTool env, test-env isolation, flock contention). Those also fail on origin/dev on this host.
  • bun run typecheck in packages/core is clean, and oxlint is clean on the touched files.

Visual

<svg xmlns="http://www.w3.org/2000/svg" width="240" height="72" viewBox="0 0 240 72" fill="none" font-family="ui-monospace,monospace" font-size="11">
  <style>
    .k{animation:k 3s ease-in-out infinite}
    @keyframes k{0%,30%{opacity:.25}50%,85%{opacity:1}100%{opacity:.25}}
    @media (prefers-reduced-motion:reduce){.k{animation:none;opacity:.2}}
  </style>
  <circle cx="20" cy="22" r="5" fill="currentColor" opacity=".95"/>
  <text x="34" y="26" fill="currentColor" opacity=".9">custom</text>
  <text x="150" y="26" fill="currentColor" opacity=".5">ready</text>
  <circle cx="20" cy="50" r="5" stroke="currentColor" stroke-width="1.5" opacity=".5"/>
  <circle class="k" cx="20" cy="50" r="5" fill="currentColor"/>
  <text x="34" y="54" fill="currentColor" opacity=".9">openai</text>
  <text x="150" y="54" fill="currentColor" opacity=".5">needs key</text>
</svg>

Requested by: @rekram1-node (Aiden Cline via Slack)


Added by the opencode bot (AI-generated).

Fixes #54686

Related: #42790

@opencode-agent opencode-agent Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fix works. I ran opencode models from source with a config that only sets a timeout on openai. On the base it lists all 76 openai models with no credential; with this PR only the opencode models remain. The two new tests fail on the base and pass here, test/config/provider.test.ts passes (41 tests), and tsc -b in packages/core is clean.

The public API is unchanged: no config schema, SDK, HTTP or plugin changes.

One gap is worth addressing: a credential sent in a configured header doesn't count (see the inline comment).

Fixes #54686
Related: #42790

provider.activation = "enabled"
// A provider first declared here (including a `canonical` copy) or by an integration needs no connection.
// Overriding an existing provider's settings does not authenticate it unless the settings carry a credential.
if (

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only settings.apiKey, authToken and accessToken count as a credential here. If someone points an existing catalog provider at a gateway and authenticates with a configured header (headers: { Authorization: "Bearer …" } or x-api-key), that provider was usable before this change but now disappears until they add a connection or a dummy apiKey. #42790 describes exactly that header-auth gateway setup, though for custom provider IDs. Should a configured auth header also count as a credential here, or is the regression intended? Either way, a shared helper with hasConfiguredAuth in model-resolver.ts would stop the two checks from drifting apart.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Setting provider.<id>.options.baseURL changes which model opencode picks by default

1 participant