Skip to content

agents/models/opencode: OpenCode needs to let plugins supply an AI SDK provider on workerd #2777

Open

Description

@mattzcarey

agents/models/opencode needs to give OpenCode an AI SDK provider from a plugin. On workerd, OpenCode 2.2.1 doesn't let us do that, so the OpenCode upgrade (#2776) is on hold. This issue explains the problem and the change we'd like from OpenCode.

How it works today

createAI() returns an OpenCode plugin. The plugin adds a cloudflare provider whose models are built by agents/models/ai-sdk: Workers AI, AI Gateway catalog models like openai/gpt-5.5 and anthropic/claude-opus-4.8, and dynamic routes.

Every OpenCode provider has to name a package. OpenCode loads it through sdk hooks and then builds the model through language hooks. We can't install a package on workerd, so we name one that OpenCode already bundles, and our language hook replaces the model it would build. Up to 2.2.0 that package was @ai-sdk/vercel. OpenCode 2.2.1 stopped bundling it (fc6922f), so every turn on 2.2.1 now fails with provider.no-route.

The only bundled packages left with an in-process sdk hook are gitlab-ai-provider and @ai-sdk/github-copilot. Switching to gitlab-ai-provider works, and #2776 has it, but we don't want to load Cloudflare models through a GitLab package.

Why we can't use our own package

We'd rather name our own package and set the SDK ourselves:

const CARRIER_PACKAGE = "aisdk:agents/models/opencode";

await ctx.aisdk.hook("sdk", (event) => {
  event.sdk = {
    languageModel: (id: string) =>
      asLanguageModelV3(aiSdk.languageModel(id, models.get(id)))
  };
}, { providerID });

On OpenCode 2.2.1 and main (149767c), this never gets to run:

  • sdk hooks run in the order plugins register (aisdk.ts#L312).
  • The built-in provider plugins register before external ones (plugin/internal.ts#L237). The last of them is DynamicProviderPlugin (plugin/provider.ts#L63).
  • DynamicProviderPlugin handles every package that no earlier hook claimed. It runs npm.add() and then importModule() (plugin/provider/dynamic.ts, sdk-factory.ts). Neither works on workerd:
    • The workerd profile doesn't replace Npm.Service, so the real installer runs and fails with Cannot destructure property 'LRUCache' of 'require(...)'.
    • importModule always rejects on workerd (runtime/import.workerd.ts), so a file:// package doesn't work either.

The npm loader always runs before any external plugin's sdk hook, so an external plugin has no way to provide a package of its own.

What we'd like from OpenCode

  1. Run the npm loader as a fallback. It should only run when no sdk hook, built-in or external, has set event.sdk. One way: AISDK.language calls loadSDKFactory itself when runSDK returns no SDK, instead of DynamicProviderPlugin sitting in the hook list.
  2. Optionally, stub Npm.Service in the workerd profile, the same way Pty, FileSystem and Vcs are stubbed in server/src/workerd.ts. Then an unknown package fails with a clear "unavailable on workerd" error instead of the LRUCache crash.

The first change is enough for us. I tested it by removing DynamicProviderPlugin from the built-in list in a local copy of OpenCode 2.2.1, with the code above in agents/models/opencode:

  • The OpenCode harness tests (31) and model tests (5) passed.
  • A deployed Worker ran Workers AI (@cf/moonshotai/kimi-k2.7-code), openai/gpt-5.5 and anthropic/claude-opus-4.8 through AI Gateway, with plain prompts and tool calls.

On stock 2.2.1, the same code fails 14 of the 31 harness tests with the LRUCache error.

Until then

  • Users on OpenCode 2.0.24 through 2.2.0 are fine.
  • Users on 2.2.1 get provider.no-route from agents/models/opencode.
  • fix(agents): upgrade opencode to 2.2.1 #2776 stays a draft until OpenCode ships the fallback change. Then we'll switch to aisdk:agents/models/opencode and drop the GitLab carrier.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions