Skip to content

[RFC] OpenCode 2 plugin API support - #32

Draft
AlexanderWillner wants to merge 6 commits into
yuseferi:mainfrom
AlexanderWillner:rfc/opencode-v2-plugin-api
Draft

AlexanderWillner wants to merge 6 commits into
yuseferi:mainfrom
AlexanderWillner:rfc/opencode-v2-plugin-api

Conversation

@AlexanderWillner

@AlexanderWillner AlexanderWillner commented Sep 23, 2026 •

Copy link
Copy Markdown

Summary

This PR adds OpenCode 2 plugin API support while retaining the existing OpenCode 1 server entrypoint.

It also fixes modality discovery for LiteLLM chat models. When LiteLLM does not report capability metadata, the plugin now registers the model as text-only instead of relying on OpenCode's image-capable fallback. The model cache version is incremented so stale entries are refreshed after the upgrade.

Type of change

  • Bug fix (non-breaking)
  • OpenCode 2 compatibility
  • Documentation and tests

Checklist

  • npm run typecheck passes
  • npm test passes
  • No new runtime dependencies beyond the OpenCode 2 plugin package
  • README updated for the behavior change
  • CHANGELOG.md updated under ## [Unreleased]
  • Commit messages follow Conventional Commits

How was this tested?

  • npm test: 67 tests passed
  • npm run typecheck
  • git diff --check
  • Live OpenCode 2 smoke test against the configured LiteLLM route
  • Text completion succeeded
  • An image attachment no longer produced the mmproj backend error or retry loop

The model catalog now reports level-1-lokal with input: ["text"] and output: ["text"] when capability metadata is absent. A route with a working multimodal projector can opt in with modelCapabilities: { "model-id": { "supports_vision": true } }.

Maintainer questions

  1. Should the package support OpenCode 1 and 2 in one release, or should V2 support be a major-version transition?
  2. Is @opencode/plugin the preferred dependency and API surface for V2 plugins?
  3. Does the V2 provider and authentication integration match the expected configuration and /connect behavior?
  4. Are provider transform/reload and event subscription the intended lifecycle hooks for discovering new models?

Related to #31. This RFC intentionally does not close the issue.

@coderabbitai

coderabbitai Bot commented Sep 23, 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

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.

Default LiteLLM chat models to explicit text-only modalities when capability metadata is absent, and invalidate stale model caches. Add regression coverage for OpenCode 2 discovery and cache upgrades.
Explain the conservative default for LiteLLM models without modality metadata and record the fix in the Unreleased changelog.
@yuseferi

Copy link
Copy Markdown
Owner

Independently verified the loader contract behind this PR's hybrid approach on two real runtimes (probe plugins under opencode models --print-logs --log-level DEBUG, plus opencode debug v2). Confirming the design and weighing in on the maintainer questions.

Verification result

  • Stable 1.18.31 and newest dev (0.0.0-dev-202609221946) both run the V1 loader only. A default export that is an object with server() loads; a { id, setup }-only default (no server()) is rejected with must default export an object with server(), and its setup/catalog.transform never fire.
  • LiteLLMPluginDefinition = { ...Plugin.define({ id, setup }), async server(input) { return LiteLLMPlugin(input) } } therefore loads on current runtimes via server(), with the V2 setup carried inert until upstream wires the V2 loader. This is the right shape — the hybrid works today and is forward-compatible. (I did not run this branch end-to-end; this confirms the export contract it relies on.)

Maintainer questions

  1. One release or a major-version transition? One release. The object-with-server() form satisfies both contracts simultaneously, so V1 keeps working while V2 rides along dormant — no major bump needed just for the transition.
  2. Is @opencode/plugin the preferred V2 dependency/surface? Yes for this design. The older @opencode-ai/plugin/v2/promise CatalogDraft only exposes update/remove — no model add — so it cannot register discovered models, and its setup isn't invoked by any shipping runtime. @opencode/plugin@2.x has provider.transform + editor.add + event.subscribe, which is what issue OpenCode 2 plugin API compatibility #31 actually asks for.
  3. /connect + auth integration: matches what I saw — the V1 server() path resolves the stored credential the same way it does today; nothing in the loader verification contradicts it.
  4. Are provider transform/reload + event subscribe the intended lifecycle hooks? They're the correct V2 hooks on the @opencode/plugin@2.x surface. Caveat worth a note in the README: on the runtimes I tested those V2 hooks are not yet invoked (V1 loader only), so the active code path today is still server() + the V1 config/event hooks. The V2 path becomes live when OpenCode flips the loader.

The modality fix (always emit modalities, defaulting to text-only, so llama.cpp routes don't receive image parts) and the cache-version bump both look correct and address a real failure mode.

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.

2 participants