Repository navigation
agents/models/opencode: OpenCode needs to let plugins supply an AI SDK provider on workerd #2777
Description
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:
sdkhooks 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 isDynamicProviderPlugin(plugin/provider.ts#L63). DynamicProviderPluginhandles every package that no earlier hook claimed. It runsnpm.add()and thenimportModule()(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 withCannot destructure property 'LRUCache' of 'require(...)'. importModulealways rejects on workerd (runtime/import.workerd.ts), so afile://package doesn't work either.
- The workerd profile doesn't replace
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
- Run the npm loader as a fallback. It should only run when no
sdkhook, built-in or external, has setevent.sdk. One way:AISDK.languagecallsloadSDKFactoryitself whenrunSDKreturns no SDK, instead ofDynamicProviderPluginsitting in the hook list. - Optionally, stub
Npm.Servicein the workerd profile, the same wayPty,FileSystemandVcsare stubbed inserver/src/workerd.ts. Then an unknown package fails with a clear "unavailable on workerd" error instead of theLRUCachecrash.
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.5andanthropic/claude-opus-4.8through 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-routefromagents/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/opencodeand drop the GitLab carrier.
Activity