anomalyco / anomalyco/opencode
[Bug]: Plugin and provider loaders cache the same package under two keys and never refresh an existing install
@rekram1-node is already working on this.
Since Sep 11, 2026.
- Dominant language
- TypeScript
- Stars
- 209k
- Forks
- 27.5k
- Avg merge
- 7h 2m
- Merged PRs (30d)
- 384
Description
Summary
Opencode can keep loading an old version of a plugin/provider package even though npm reports the latest version as installed. Two things combine: the cache is keyed by the exact specifier string, and Npm.add returns an existing directory without checking whether it is still current.
Environment
- OS: Ubuntu 24.04.4 LTS
- opencode: 1.18.30
- relevant package:
opencode-cmd-provider(@latestdir at 1.7.3, bare dir at 1.6.2;npmreports 1.7.3 as latest)
Problem
1. Cache is keyed by the exact specifier string
Npm.add derives the cache directory from the raw package string:
// packages/core/src/npm.ts
const directory = (pkg: string) => path.join(global.cache, "packages", sanitize(pkg))
The two loaders pass different strings for the same package:
-
plugin loader appends
@latest:// packages/opencode/src/plugin/shared.ts const pkg = hit?.name && hit.raw === hit.name ? `${hit.name}@latest` : spec -
provider loader uses
model.api.npmverbatim (whatever the plugin/provider declared):// packages/opencode/src/provider/provider.ts const item = await Npm.add(model.api.npm)
So a single plugin that registers its provider by bare name produces two directories:
~/.cache/opencode/packages/opencode-cmd-provider@latest # plugin
~/.cache/opencode/packages/opencode-cmd-provider # runtime provider
2. Existing installs are never refreshed
// packages/core/src/npm.ts
if (yield* afs.existsSafe(path.join(dir, "node_modules", name))) {
return resolveEntryPoint(name, path.join(dir, "node_modules", name))
}
Once the bare directory exists it is returned forever, at the version it was first installed. Updating the package moves only the @latest copy forward; the bare copy keeps the old version and is still what opencode loads at runtime.
Impact
The effective state is confusing and user-visible: npm reports the latest version as installed, but opencode runs the old one. Anything that depends on the two halves of one plugin matching — capability declarations vs. the code that enforces them, feature additions, bug fixes — can silently use stale behavior. Deleting the stale directory forces opencode to re-install the current version, which confirms the cause.
Why the plugin loader / provider loader should be reconciled
A plugin and the provider it registers are conceptually one dependency. Keying them separately and never reconciling versions makes divergence possible for any plugin that registers a provider by an unpinned (or differently formatted) specifier. A plugin author can work around this by pinning a version, but opencode should not depend on that discipline to keep a plugin internally consistent.
Possible directions
- Key the cache by resolved package name (or name + resolved version), so
fooandfoo@latestresolve to the same installed artifact instead of two. - Have
Npm.addverify the installed version against the requested spec and re-resolve when it no longer satisfies it, instead of short-circuiting on mere existence. - Ensure a plugin's runtime provider is loaded from the same resolved package the plugin came from.
Related
Plugin-side report (unpinned provider specifier makes this reachable): https://github.com/rashidrazak/opencode-cmd-provider/issues/149
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.