npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

opencode-auto-jev

v0.1.1

Published

OpenCode plugin that adds an Auto (Jev) virtual model which routes each prompt to a configured real model using TypeSafe AI (Jev).

Readme

opencode-auto-jev

npm version

An OpenCode plugin that adds an Auto (Jev) virtual model to the normal model picker. When you pick Auto, each of your turns is sent to TypeSafe AI (Jev), which picks one of your configured real models for that prompt. OpenCode then calls that real model as usual — native credentials, auth adapters, streaming, tools and accounting all keep working.

There is no OpenCode fork and no UI patch. The plugin only:

  1. registers a virtual provider/model (auto-jev/auto, displayed as Auto (Jev)) through the config hook, and
  2. rewrites chat.message's mutable output.message.model to a real provider/model before the model call.

Nothing is routed unless you actually selected Auto. Manually selected real models are never touched.


Quick start

The plugin is available on npm. OpenCode installs the package and its dependencies automatically from your configuration; no local checkout or build is needed. Use OpenCode 1.18.32 or newer.

The TypeSafe API key is supplied only through the plugin's apiKey option. There is no environment-variable support: the plugin never reads, writes or requires TYPESAFE_API_KEY (it also pins the TypeSafe base URL so TYPESAFE_BASE_URL cannot redirect the request).

Merge the following into your OpenCode config (~/.config/opencode/opencode.json or a project opencode.json). Replace REPLACE_WITH_YOUR_TYPESAFE_API_KEY with your key and choose candidate models you have connected:

{
  "$schema": "https://opencode.ai/config.json",
  "model": "auto-jev/auto",
  "plugin": [
    [
      "opencode-auto-jev@latest",
      {
        "apiKey": "REPLACE_WITH_YOUR_TYPESAFE_API_KEY",
        "fallback": "anthropic/claude-sonnet-4-5",
        "candidates": [
          {
            "providerID": "anthropic",
            "modelID": "claude-sonnet-4-5",
            "description": "Strong general coding model for refactors, multi-file changes and debugging."
          },
          {
            "providerID": "anthropic",
            "modelID": "claude-haiku-4-5",
            "description": "Very fast, cheap model for small edits, simple questions and formatting."
          }
        ]
      }
    ]
  ]
}

Prefer not to inline the secret? OpenCode substitutes {file:...} references in config text before the plugin sees its options, so you can point at a private file instead:

"apiKey": "{file:~/.secrets/typesafe-api-key}"

Key points:

  • apiKey is required. Missing, blank, whitespace-only or non-string values fail plugin setup with an option-named error (the value is never echoed). Whether you inline the key or use {file:...}, it must be a non-empty string.
  • Merging, not replacing. If you already have plugin, provider, model or agent settings, add this entry to your existing plugin array instead of overwriting the file. The plugin never rewrites your config; it only adds its own virtual provider at startup. Everything else is preserved.
  • Candidates must already be connected. Each providerID/modelID pair must be a provider you have actually authenticated under OpenCode. Auto only ever routes to a model present in OpenCode's live catalog, so an unconnected or disabled provider is silently ineligible (and reported in the error if nothing is left).
  • "model" is optional. Setting "model": "auto-jev/auto" makes Auto the default for new sessions; you can also just pick Auto (Jev) from the model picker (/models). If you leave model out, nothing changes until you select Auto.
  • enabled_providers users: if your config sets enabled_providers, you must add "auto-jev" to that list yourself, because OpenCode then ignores every provider not listed. This plugin does not modify your provider policy.

Quit and restart OpenCode after editing the config, then choose Auto (Jev) in the model picker. A fuller configuration is in examples/opencode.json.

Package versions

The README and example use opencode-auto-jev@latest, which resolves npm's latest release tag instead of hard-coding a version. The version badge above also reads from npm automatically (subject to caching), so neither needs a version-number edit for each release.

For reproducible installs, replace latest with an exact published version from the npm package page. OpenCode manages plugin installation and caching; @latest does not hot-reload a running session. Restart OpenCode after updating plugins.


Configuration options

| Option | Default | Meaning | | --- | --- | --- | | apiKey | required | TypeSafe API key (non-empty string). Config-only — no env fallback. Sent to TypeSafe as the Authorization header. | | providerID | auto-jev | Virtual provider id. Must not already exist in your config. | | modelID | auto | Virtual model id. | | displayName | Auto (Jev) | Name shown in the model picker. | | candidates | required | 2–255 { providerID, modelID, description } entries. description is sent to Jev. | | fallback | none | Candidate key (providerID/modelID) used when Jev fails or is unsure. Must be one of candidates. Without it, Auto fails closed. | | jevModel | jev-latest | TypeSafe model name. | | timeoutMs | 15000 | Per-attempt Jev timeout (1000–120000). | | maxRetries | 1 | Bounded Jev retries (0–5). | | minConfidence | 0 | Below this Jev confidence, use the fallback (or fail closed). 0..1. See Tuning. | | maxPromptChars | 4000 | Maximum prompt text sent to Jev. | | notify | true | Show an Auto → provider/model toast after routing. | | baseURL | https://api.typesafe.ai | TypeSafe API root. Always explicit so TYPESAFE_BASE_URL cannot redirect the key. |

Invalid configuration fails loudly at plugin load with the offending option named.

Tuning

minConfidence defaults to 0, which trusts any valid closed-set choice Jev returns. If you prefer to fall back more often, raise it (for example 0.6): any turn where Jev's reported confidence is below the threshold uses your fallback (or fails closed if none is configured). This is a preference knob, not a claim about routing quality.


Behaviour

  • Routing happens once per user turn, before the model call, by rewriting output.message.model. It never reruns per tool iteration, so every tool result in a turn stays on the same model.
  • Auto stays selected for the session. Once you pick Auto, OpenCode keeps Auto as the session's model and every subsequent turn is re-routed. Selecting a real model manually immediately stops routing — the plugin detects that the incoming message model is not the alias and does nothing.
  • Fails closed. If Jev is unreachable/times out/returns an unknown choice/an invalid confidence, Auto uses your configured fallback if it is available for that prompt; otherwise the turn is not sent and you get a clear error.
  • Candidates are verified against the live OpenCode catalog at routing time. Models that are missing, disabled, deprecated, lack tool-calling, or cannot read the prompt's modalities are removed before Jev ever sees them. Jev can only ever choose one of the configured candidates. Attachment modality filtering removes candidates that cannot read the attachment; if no candidate can, the turn fails with an actionable error rather than routing to an incapable model.
  • Single eligible candidate short-circuits. If only one configured candidate is available for a given prompt, Auto selects it deterministically and does not call Jev (a one-label choice carries no routing information).
  • Incompatible variants are cleared. The alias has no variants, so a stale variant never leaks onto the real model.
  • Concurrency safe. Routing state is per-call; concurrent sessions do not interfere.
  • The virtual provider points at a reserved loopback port (127.0.0.1:9). If an alias were ever invoked without routing, the guard makes that call fail locally instead of reaching a real endpoint; it is a failure guard, not a guarantee that no local listener exists.

Session resume and navigation

Auto persistence is only confirmed through the HTTP/session API and the active-session source path. When you navigate away from a session and back, or restart OpenCode, the TUI re-hydrates the picker from the last user message's model — which the plugin rewrote to the real model that actually served that turn. So after resuming or reloading a session you may need to reselect Auto (Jev); the plugin does not persist the alias across UI reloads or navigation. Manual selection always wins.


Cost and privacy

When Auto is used, a turn makes an additional TypeSafe (Jev) request before the model call, and normal OpenCode requests to the chosen real provider (which may include multiple requests when the agent loops over tools or retries). It is not a fixed "exactly two" request count.

What is sent to Jev:

  • the text you typed in this turn (bounded to maxPromptChars), and
  • the candidate providerID/modelID + description strings.

What is never sent to Jev:

  • file contents, directory listings or attachments (only their modality is considered locally),
  • conversation history, tool outputs, the system prompt,
  • stored credentials or API keys (your apiKey is used only as the Authorization header; it is never placed in the request body, logs or errors).

Synthetic text OpenCode adds for file reads, and ignored pastes, are excluded too. This exclusion covers stored credentials and file data; it cannot detect a secret you type directly into the prompt, so avoid pasting secrets into an Auto turn. Attachment-only or empty prompts are rejected with an actionable error rather than routing blindly.

Jev request and response logs

Every Jev HTTP attempt is logged at info level under service opencode-auto-jev, including retries. Jev HTTP entries contain the request body (prompt text, candidate descriptions and model), the response body (including error responses), HTTP status and elapsed milliseconds. Match each request and response using requestID. Transport failures have an error entry. There are no Jev HTTP entries when routing is bypassed or only one candidate is eligible.

On macOS, follow these entries while using OpenCode Desktop:

tail -f ~/.local/share/opencode/log/*.log | grep 'Jev HTTP'

These logs contain prompt text and API response contents. Headers are excluded and occurrences of the configured API key are redacted. Other secrets you type into a prompt are not automatically detected. Logging failures do not prevent routing. Restart OpenCode Desktop after rebuilding the plugin to load changes.

Where the key is stored

Your apiKey is a plaintext secret stored in your OpenCode config (or in the {file:...} it points to). OpenCode may surface config values in places such as its config endpoints, logs or UI, so:

  • prefer a global/private config file or a {file:...} reference to a file only you can read,
  • never commit the key or a config containing it to a repository.

The key is transmitted to TypeSafe as the request's Authorization header; "never sent to Jev" above refers to it not being included in the routing prompt payload — it is, of course, sent to TypeSafe to authenticate.

Text-only for now. The virtual alias advertises text-input only, and the MVP does not run a verified multimodal picker. Classifiable non-text attachments (image/pdf/audio/video) are used locally to filter candidates by capability and are never forwarded to Jev; unclassifiable MIME types are rejected. Jev usage may be billed by TypeSafe; the downstream model request is billed by your provider as usual.


Troubleshooting

| Symptom | Cause / fix | | --- | --- | | Auto (Jev) missing from the picker | Config not reloaded — restart OpenCode. Check the provider id isn't already in use, and that you added "auto-jev" to enabled_providers if you set it. | | Virtual provider "..." already exists | You already define that provider id. Rename it or change providerID. | | Auto has no available candidate... | None of your candidates are connected/enabled or capable of this prompt. | | Auto needs some typed text... | Attachment-only prompt. Type text or pick a model manually. | | Auto cannot route attachments of type ... | Unsupported/unknown attachment MIME type. | | Auto could not read the OpenCode provider catalog... | OpenCode was not reachable while routing. Retry or select a model manually. | | missing required option "apiKey"... | Add a non-empty apiKey to the plugin options (inline or via {file:...}). | | Turn fails with a Jev error | Set fallback, or check the apiKey value and network access. |


Development

From a local checkout:

npm ci
npm run build

To test local changes, replace opencode-auto-jev@latest in your plugin config with file:///absolute/path/to/opencode-auto-jev/dist/index.js, keeping the same options. Rebuild and restart OpenCode after changes.

npm run typecheck   # strict TypeScript
npm run build       # emit ./dist
npm run test        # build + unit tests + real-runtime smoke tests
npm run test:unit   # mocked unit tests only
npm run test:smoke  # build + real opencode runtime + local mock endpoints
npm pack --dry-run  # inspect the published file list

test and test:smoke build first, so a clean checkout has an up-to-date dist. The smoke suite starts an isolated opencode serve (its own HOME/XDG/config) using the public launcher on PATH (override with OPENCODE_BIN) and local mock TypeSafe/chat servers. It uses no real credentials and makes no paid API calls. Set AUTO_JEV_SMOKE=0 to skip it.

Manual smoke checklist

With a local build, your own provider credentials, and a valid apiKey in the plugin options (inline or via {file:...}):

  1. First turn from home — start OpenCode, pick Auto (Jev), and send a plain text prompt. The reply should come from one of your candidates; the toast/log shows Auto → provider/model.
  2. Second turn (unspecified model) — send another prompt without touching the picker. It should be re-routed by Jev (Auto stays sticky within the session).
  3. Navigate away and back — open another session and return. The footer may now show the real model from the last turn; reselect Auto (Jev) and confirm routing resumes.
  4. Restart — quit and relaunch OpenCode, resume the session, reselect Auto if needed, and confirm routing works.
  5. Manual escape — pick a real model and send a prompt; confirm no Jev request is made (watch the log/toast) and the real model answers.

No visual QA was performed by the implementation worker; this checklist is the required human confirmation.

Publishing

Maintainers with npm publish access can release a new version with:

npm run typecheck
npm test
npm version patch                 # or minor / major, as appropriate
npm publish --access public

The prepack script builds dist automatically before publishing. A normal release updates npm's latest tag, which the quick start and example reference. For prereleases, use a prerelease version and a separate tag such as npm publish --access public --tag next to keep them out of latest.

Tested version

OpenCode 1.18.32 (@opencode-ai/plugin 1.18.32, opencode-ai 1.18.32, @opencode-ai/sdk 1.18.32), @typesafe-ai/sdk 0.6.0, Node 22. The plugin declares engines.opencode >= 1.18.32; earlier versions are not verified.