Skip to content

Plugins with npm dist-tags other than @latest never update on restart #12312

Closed

Description

@ErcinDedeoglu

Problem

When a plugin is configured with an npm dist-tag other than @latest (e.g., @dev, @next, @canary, @beta), OpenCode locks it to the first resolved version and never checks for updates on subsequent startups.

Config:

{
  "plugin": [
    "oh-my-opencode@latest",
    "oc-solomemory@dev"
  ]
}

Expected: Both plugins check the registry on startup and update if a newer version is published under their respective tag.

Actual: @latest correctly checks for updates every startup. @dev (and any non-latest tag) is cached permanently and never re-resolved — even if a newer version is published under the dev tag on npm.

Root Cause

In packages/opencode/src/bun/index.ts, the install logic only checks the registry for the literal string "latest":

if (!modExists || !cachedVersion) {
  // continue to install
} else if (version !== "latest" && cachedVersion === version) {
  return mod  // ← Any non-"latest" tag: string match against cache, return immediately
} else if (version === "latest") {
  const isOutdated = await PackageRegistry.isOutdated(pkg, cachedVersion, Global.Path.cache)
  if (!isOutdated) return mod  // ← Only "latest" checks the registry
}

After the first install, package.json in the cache stores the tag name (e.g., "dev") as the dependency value. On subsequent startups: cachedVersion === version → "dev" === "dev" → true → returns cached module immediately without ever checking the registry.

Meanwhile, @latest goes through PackageRegistry.isOutdated() which queries bun info {pkg} version and does a semver comparison — so @latest always gets updates.

Suggested Fix

npm dist-tags are mutable pointers, not fixed versions. All dist-tags should be re-resolved on startup, not just "latest". A simple heuristic:

function isDistTag(version: string): boolean {
  // Dist-tags are simple lowercase strings: "latest", "dev", "next", "canary", "beta"
  // Actual versions contain dots/digits: "1.0.0", "^1.0.0", "1.0.0-dev.abc123"
  return /^[a-z][a-z0-9-]*$/i.test(version) && !semver.valid(version)
}

Then in the install logic:

if (!modExists || !cachedVersion) {
  // continue to install
} else if (isDistTag(version)) {
  // Always check registry for dist-tags (@latest, @dev, @next, etc.)
  const isOutdated = await PackageRegistry.isOutdated(pkg, cachedVersion, Global.Path.cache)
  if (!isOutdated) return mod
  log.info("Cached version is outdated, proceeding with install", { pkg, cachedVersion })
} else if (cachedVersion === version) {
  return mod  // Exact pinned version, use cache
}

Note: PackageRegistry.isOutdated() currently resolves only the latest tag via bun info {pkg} version. For non-latest tags, it would need to resolve the specific tag (e.g., bun info {pkg}@dev version or query npm view {pkg} dist-tags).

Workaround

Force-update manually:

npm install oc-solomemory@dev --prefix ~/.cache/opencode

Environment

  • OpenCode: latest
  • OS: Linux (ARM64)
  • Plugin: oc-solomemory@dev

Activity

  1. github-actions commented on Feb 5, 2026

    @github-actions
    Contributor

    This issue might be a duplicate of existing issues. Please check:

    Feel free to ignore if none of these address your specific case.

  2. github-actions commented on Apr 7, 2026

    @github-actions
    Contributor

    To stay organized issues are automatically closed after 90 days of no activity. If the issue is still relevant please open a new one.

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

Metadata

Metadata

Assignees

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