Skip to content

feat(v2): declare providers in the plugin options like the config does - #48

Closed
zyc wants to merge 1 commit into
yuseferi:mainfrom
zyc:feat/plugin-options-providers
Closed

zyc wants to merge 1 commit into
yuseferi:mainfrom
zyc:feat/plugin-options-providers

Conversation

@zyc

@zyc zyc commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Summary

On OpenCode 2, providers declared under the config's providers key are registered by the host after plugin setup, so a one-shot opencode run on a cold cache resolves its model before the plugin has published anything (Model unavailable, see #46 and anomalyco/opencode#57421). Declaring the provider in the plugin's own options avoids that, but the flat options describe a single provider, and the host keeps one instance per plugin id (a second entry of the same package is dropped). So one proxy cannot be split into several providers on a cold start, and two different proxies cannot be used at all.

This accepts providers in the plugin options, keyed by provider id and written the way the config's providers key is (name, package, settings, headers). Each one goes through the same code path as a provider the host declares, with one difference: it exists during setup, so a cold start already finds its models. They are discovered concurrently, so a slow gateway costs the longest wait instead of the sum (the host gives plugin activation about 5 s, and one discovery can wait up to 20 s).

Each provider is self-contained: address, credentials, filters and headers come from its own declaration, and the plugin-wide options are not applied to it.

This replaces the first version of this PR, which used a list with a providerID and merged each entry over the shared options. Reusing the config's own shape needs about half the code, has no inheritance rules to document, makes a repeated id impossible and gives each provider its own package and headers for free.

Type of change

  • 🐛 Bug fix (non-breaking)
  • ✨ New feature (non-breaking)
  • 💥 Breaking change
  • 📝 Documentation only
  • 🔧 Internal / refactor

Checklist

  • npm run typecheck passes
  • No new runtime dependencies (or justified in this PR description)
  • README updated if public API or behavior changed (new "Declaring providers in the plugin options" section)
  • CHANGELOG.md updated under ## [Unreleased] (not touched: the file is generated by semantic-release from the commit message)
  • Commit messages follow Conventional Commits

Compatibility with 1.7.1

Purely additive: a new key is read, and without it (or with a value that declares nothing) nothing changes. The diff is 53 additions and 2 deletions, all in src/plugin/v2.ts (a helper, the start of setup and a defaulted parameter of reconcileProviderSources). The OpenCode 1 entry point, src/utils and src/types are untouched.

I checked it against the published 1.7.1 (the npm tarball is byte for byte the v1.7.1 tag) with a differential run: the same 21 scenarios against 1.7.1 and against this branch, comparing the first publication, the final registry, every request (URL, method, headers) and the logs. The scenarios are flat options (plain, with filters, custom id and headers), env fallback, no gateway, a host provider that shows up after setup, two host providers with their own gateways, keys and package, flat options acting as defaults under a host provider, a credential connected through the dialog, a warm cache on the second start, session.created, the usages the README documents (per-model modelCapabilities and modelCompatibility, a separate Anthropic-runtime provider for Claude models, a mode: "responses" model, gateway headers, a key saved by /connect, LITELLM_MASTER_KEY), and three providers values that declare nothing. All 21 are identical. As a sanity check of the harness, an injected regression (reconcile ignoring every host provider) is flagged in 13 of the 14 scenarios it ran on at the time.

I repeated the comparison on real OpenCode binaries, published 1.7.1 against 1.7.1 plus this change, cold and warm: OpenCode 1 (1.18.29 and 1.19.3) with four config forms, and OpenCode 2 (2.1.0 and 2.0.32) with the documented ones (config providers with one or two providers, flat plugin options, env only, flat options as defaults under a config provider). Everything the host reports is identical, including the cold-start behavior of config providers (unchanged, this PR does not touch it).

Without providers the sequence of awaits in setup is the same as in 1.7.1: the only new await sits inside the branch that declares providers. The only input whose behavior changes is a non-empty object under options.providers, which 1.7.1 handed on as an inert setting (nothing in 1.7.1 reads a providers option).

Notes for review

  • If the config declares the same id, the plugin options win. That holds whether the host already shows the provider during setup or only afterwards (both are covered).
  • activation is always enabled for a declared provider. name defaults to the id.
  • The host reflects the plugin's own publication back as a provider, so reconcile skips the declared ids.

How was this tested?

  • npm test: 172 passed (157 before, 15 new in test/plugin-v2-options-providers.test.ts). The new tests use a host stub whose registry reflects what the plugin publishes, the way a real host does.
  • Mutation check: each of these changes makes at least one new test fail when reverted: label from name, concurrent discovery, plugin-wide options not applied to declared providers, per-provider package, activation, non-object entries ignored, per-provider settings, per-provider headers, providers consumed only when it declares something, reconcile skipping declared ids.
  • Against a LiteLLM proxy with two slices of one catalog (includeModels / excludeModels by prefix), on OpenCode 2.1.0 with an empty plugin cache: opencode run --standalone -m <provider>/<model> answered on the first run for both providers, with {env:...} inside settings. In a long session started cold, each provider kept its own slice.

@coderabbitai

coderabbitai Bot commented Oct 20, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@zyc
zyc force-pushed the feat/plugin-options-providers branch from bee3259 to 40f062c Compare October 20, 2026 20:14
@zyc zyc changed the title feat(v2): declare several providers in the plugin options feat(v2): declare providers in the plugin options like the config does Oct 20, 2026
On OpenCode 2, providers under the config's `providers` key are registered
by the host after plugin setup, so a one-shot `opencode run` on a cold cache
resolves its model before the plugin has published anything. Plugin options
avoid that, but the flat options describe a single provider, and the host
keeps one instance per plugin id.

Accept `providers` in the plugin options, keyed by provider id and written
the way the config's `providers` key is (`name`, `package`, `settings`,
`headers`). Each one goes through the same code path as a provider the host
declares, with one difference: it exists during setup, so a cold start
already finds its models. They are discovered concurrently, so a slow gateway
costs the longest wait instead of the sum (the host gives setup about 5 s,
and one discovery can wait up to 20 s).

Each provider is self-contained: address, credentials, filters and headers
come from its own declaration, and the plugin-wide options are not applied to
it, so a provider never picks up another one's key. If the config declares
the same id, the plugin options win. Without `providers`, or with a value
that declares nothing, behavior is unchanged.
@zyc
zyc force-pushed the feat/plugin-options-providers branch from 40f062c to e6ece03 Compare October 20, 2026 21:47
@zyc

zyc commented Oct 21, 2026

Copy link
Copy Markdown
Contributor Author

Closing this. We no longer need the providers option in the plugin options: our users now run with the config exactly as the README documents (plugins plus providers.*), and the cold one-shot limitation is on the host side (anomalyco/opencode#57421). Details in #46. Thanks for the time.

@zyc zyc closed this Oct 21, 2026
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