Skip to content

About

Model aliases for OpenCode V2: automatically select the latest matching model and inspect resolved targets.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

opencode-model-aliases

npm version CI License: MIT

Model IDs go stale: previews get promoted, point releases land, and the IDs pinned in your agents keep pointing at last month's model. This OpenCode v2 plugin adds floating aliases to the model catalog: a stable ID such as opencode/zen-plan that always resolves to the newest model matching your rules.

Docs vs releases. This README follows the default branch (master), which can be ahead of the published npm package; the GitHub Releases page lists what has shipped.

Concrete IDs vs aliases

A concrete model ID (github-copilot/claude-sonnet-4.5) pins one model you review and change yourself; OpenCode resolves it natively, no plugin needed. An alias earns its keep when you mean a policy, not a model: "the newest free model with tools and a large context", "the current Sonnet, whichever point release it is". The policy outlives the model list; the alias keeps resolving to whatever satisfies it today.

Quick start

Add the plugin and one alias to opencode.json:

{
  "plugins": [{
    "package": "opencode-model-aliases@latest",
    "options": {
      "aliases": {
        "github-copilot/claude-sonnet": { "match": "github-copilot/claude-sonnet-*" }
      }
    }
  }]
}

Run /model-aliases to see what each alias resolved to, then select github-copilot/claude-sonnet like any other model; requests go to the real winning model.

OpenCode caches installed plugin packages: restarting alone doesn't install a newer release. Update with opencode plugin update opencode-model-aliases@latest.

Examples

Each alias key is <provider>/<alias-id>. Every pattern must use the same literal provider as its key: an alias never selects a model from another provider.

Follow a family and skip previews or a specific model with exclude, in .opencode/opencode-model-aliases.jsonc:

// .opencode/opencode-model-aliases.jsonc — follow a family
{
  "$schema": "https://raw.githubusercontent.com/vmvarela/opencode-model-aliases/master/schema.json",
  "aliases": {
    "openai/gpt-sol":  { "match": "openai/gpt-*-sol" },
    "openai/gpt-luna": { "match": "openai/gpt-*-luna" },
    "openai/latest": {
      "match": ["openai/gpt-*", "openai/o*"],
      "exclude": ["openai/*-preview", "openai/gpt-6.1-sol-pro"]
    }
  }
}

Require capabilities — tools, image input, large context — in .opencode/opencode-model-aliases.jsonc:

// .opencode/opencode-model-aliases.jsonc — require capabilities
{
  "$schema": "https://raw.githubusercontent.com/vmvarela/opencode-model-aliases/master/schema.json",
  "aliases": {
    "github-copilot/vision": {
      "match": "github-copilot/gemini-*-flash",
      "filter": { "capabilities": { "tools": true, "input": ["image"], "output": ["text"] }, "minContext": 128000 }
    }
  }
}

Pick a model per job in opencode.json: filter the free set by what each job needs, then point agents at the aliases:

{
  "plugins": [{
    "package": "opencode-model-aliases@latest",
    "options": {
      "aliases": {
        "opencode/zen-plan": {
          "match": ["opencode/*-free", "opencode/big-pickle"],
          "filter": { "capabilities": { "tools": true }, "minContext": 256000 },
          "name": "Zen Free — Plan"
        },
        "opencode/zen-build": {
          "match": ["opencode/*-free", "opencode/big-pickle"],
          "filter": { "capabilities": { "tools": true, "input": ["text"], "output": ["text"] }, "minContext": 64000 },
          "name": "Zen Free — Build"
        }
      }
    }
  }],
  "agents": {
    "plan":  { "model": "opencode/zen-plan" },
    "build": { "model": "opencode/zen-build" }
  }
}

Filters decide eligibility, not quality. If several models qualify, the newest wins, so both aliases may currently pick the same model. *-free is a naming convention, not a price check. Only active models are eligible by default; opt into alpha/beta via filter.status.

How a winner is picked

When the plugin starts and at every catalog refresh, each alias:

  1. Matches models with match, then removes exclude matches (picomatch globs).
  2. Filters by enabled, status, capabilities and minContext. All filters must pass; missing metadata fails the filter that needs it.
  3. Selects the newest time.released. Ties use the descending model ID (gpt-6.1-sol-pro beats gpt-6.1-sol-fast). Models without release dates never win.

Selection is deterministic: same catalog, same rules, same winner. The alias gets a copy of the winner's model info under the alias ID; declaration order doesn't matter.

Options

Option Default Notes
aliases required Object of aliases; {} is valid.
strict false Fail startup if any alias doesn't resolve; strict applies at startup only (see Limits).
debug false Log each resolution as a [debug] warning.
match required Glob or list of globs: "provider/pattern".
exclude none Same format as match.
filter.status ["active"] Any of active, alpha, beta.
filter.capabilities none tools (boolean), input/output (all listed modalities required).
filter.minContext none Minimum context window, inclusive.
name generated Display name. Without it, sonnet becomes Sonnet (alias).
select latest Only { "strategy": "latest" } is supported.

Aliases can also live in .opencode/opencode-model-aliases.jsonc (JSONC comments and trailing commas are fine); the plugin uses the nearest file found from the working directory upward. Inline options win: an inline alias replaces a file alias with the same key, and inline strict/debug values win. Inline options apply when OpenCode reloads its configuration; the .jsonc file is read at plugin start, so restart after editing it.

For editor autocompletion and shape validation, schema.json ships with the package as a draft-07 JSON Schema; point the config file's $schema at the default-branch URL https://raw.githubusercontent.com/vmvarela/opencode-model-aliases/master/schema.json (file-only metadata: editors may require it, the plugin strips it, and inline plugin options never accept it). The schema checks shape only — glob compilation, the literal-provider rule and provider equality remain runtime-enforced and authoritative.

// .opencode/opencode-model-aliases.jsonc — complete example
{
  "$schema": "https://raw.githubusercontent.com/vmvarela/opencode-model-aliases/master/schema.json",
  "strict": true,
  "aliases": {
    "openai/latest": {
      "match": ["openai/gpt-*", "openai/o*"],
      "exclude": ["openai/*-preview"],
      "filter": { "minContext": 128000 }
    }
  }
}

Seeing what aliases resolved to

Run /model-aliases (or Model aliases in the command palette) for the current target and the real model ID used for requests, with unresolved reasons where a selection failed. Run /model-aliases explain <provider>/<alias-id> for the full decision: matching patterns, rejected candidates with their reasons, and whether the runner-up lost on release date or the tie-break. Nothing here calls a model. In OpenCode 2.0.22, selecting the slash suggestion leaves a trailing space; press Enter again. Without a TUI:

opencode api --standalone post /api/rpc/opencode-model-aliases/inspect --data '{"input":{}}'

The backend also exposes explain({ alias }) on the same opencode-model-aliases RPC. Explanations reflect the catalog visible to the plugin's transform: models hidden with disabled: true can still appear there and be selected by an alias (see Limits); with strict: true, a startup failure prevents the plugin and its RPC from becoming available.

Change visibility. The first resolution sets a silent baseline. When an alias later changes target, the TUI shows a toast and /model-aliases shows the previous target, the current one and when the change was detected. Editing an alias's match or filter resets its baseline: a changed alias is a config change you should see, not hunt for in logs.

Plugin ordering. The plugin resolves against the fresh catalog it receives and observes later model.updated refreshes. The inspect report is built from a fresh source catalog; a downstream rewrite of the alias modelID by another plugin can make the report return unavailable. Don't rely on plugin execution order.

Limits, failure and non-goals

  • Invalid configuration (including unknown keys and invalid globs) fails at startup, and alias/source ID collisions are fatal on every catalog replay, including startup.
  • An alias with no candidate logs a warning and is left out; other aliases keep working. With strict: true, startup fails instead. Strict applies at startup only: after a successful startup, a target loss skips only that alias, and it recovers when a matching model returns.
  • Only the latest strategy exists; the plugin can't rank by price or quality. Aliases are provider-isolated and never chain, and the plugin only reads OpenCode's catalog — it never fetches external model lists.
  • Models hidden with disabled: true in your OpenCode provider config can still be selected by an alias (#42); use exclude to keep a model out of an alias.
  • Compatibility is verified only for OpenCode 2.0.16 and 2.0.24 (real-host smoke tests in CI, tracked in #29); other host versions are not on the verified matrix.

Explicitly out of scope: prompt classification ("which prompt needs which model"), smart routing, provider proxying, credential management, retries/failover, billing, and session orchestration. What happens to a request once it leaves the plugin is OpenCode's job.

Development

Requires Node.js 22+ and pnpm 11. Use pnpm install then pnpm run verify (lint, typecheck, tests, build), pnpm run check:release for offline release checks, and the optional pnpm smoke:opencode (needs an installed OpenCode v2 CLI).

MIT License — see LICENSE.

About

Model aliases for OpenCode V2: automatically select the latest matching model and inspect resolved targets.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages