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-vision-router

v0.2.1

Published

Route pasted images to a cheap vision model in opencode so text-only main agents work from its text output.

Readme


opencode attaches a pasted image to the main (text-only) model's message and drops or errors on it before any skill or subagent runs. A skill alone cannot fix this. The only reliable fix is a plugin hook that intercepts the image at the harness level, resolves it to a readable path, and lets a cheap vision model analyze it.

opencode-vision-router is a self-contained, zero-config plugin that does exactly that — and injects the vision subagent for you, so there are no separate agent or skill files to manage.

✨ Features

  • 🖼️ Pasted-image routing — data: URLs, file:// paths, absolute paths, and OpenCode V2 raw media parts all supported.
  • 📸 Multiple images — handles several pasted images in a single message, routing each one to the vision subagent.
  • 💸 Cheap vision model — point it at any image-capable model (provider/model).
  • 🧠 Multimodal-aware — if your main model already sees images, routing is skipped automatically. Set force to always route (e.g. to a cheaper vision model).
  • 🧩 Self-contained — injects the vision subagent and system instruction at load time.
  • 🔒 Safe by default — the vision subagent can read the image but is denied edit/bash/webfetch.
  • ⚡ OpenCode 1 & 2 — one default export supports both the V1 plugin API (server()) and the V2 plugin API (setup()), plus V2 model-registry capability detection and V2 message media parts.

🧠 How it works

The plugin registers the following behavior at load time, in whichever API shape your opencode version supports:

  1. Vision subagent injection — declare the chosen model as image-capable and inject the vision subagent (V1 config hook / V2 agent.transform).
  2. Capability detection — per model, learn whether the main model can see images (V1 chat.params learning / V2 model-registry lookup), so multimodal main models are skipped unless force is set.
  3. Image rewrite — strip the image from the user message and replace it with a text pointer containing the resolved path, so a text-only model never sees the bytes:
    • V1: chat.message (primary) + experimental.chat.messages.transform (backup).
    • V2: a session.hook("context") registered in setup(), which rewrites media parts in the assembled messages immediately before each agent model request and covers both fresh attachments and history.

⚠️ V1 relies on opencode's experimental experimental.chat.messages.transform hook, which may change in future opencode versions. In V2 the equivalent is the stable context session hook.

📦 Installation

Add it to your opencode.json(c) and restart opencode — plugins are not hot-reloaded.

OpenCode 2 uses the plugins key with a package/options object:

{
  "plugins": [
    { "package": "opencode-vision-router", "options": { "model": "opencode-go/qwen3.7-plus" } }
  ]
}

OpenCode 1 uses the plugin key with a package/options tuple (OpenCode 1.18.29 or newer is required for the object-form entrypoint this plugin ships):

{
  "plugin": [
    ["opencode-vision-router", { "model": "opencode-go/qwen3.7-plus" }]
  ]
}

⚙️ Configuration

| Option | Required | Default | Description | | -------- | -------- | ------------- | ---------------------------------------------------------------------------------------------------------------------------------- | | model | yes | — | Vision-capable model as provider/model (e.g. opencode-go/qwen3.7-plus). If omitted, routing is disabled (a warning is logged). | | agent | no | vision | Name of the injected vision subagent. | | tmpDir | no | os.tmpdir() | Directory under which decoded images are cached (content-hashed, reused across calls). | | force | no | false | Route images to the vision subagent even when the main model is multimodal (e.g. to use a cheaper vision model). By default the subagent is skipped when the main model can already see images. |

🚀 Usage examples

Basic

OpenCode 2:

{
  "plugins": [
    { "package": "opencode-vision-router", "options": { "model": "opencode-go/qwen3.7-plus" } }
  ]
}

OpenCode 1 (>= 1.18.29):

{
  "plugin": [
    ["opencode-vision-router", { "model": "opencode-go/qwen3.7-plus" }]
  ]
}

Multiple plugins

{
  "plugins": [
    "@dodopayments/opencode-plugin",
    { "package": "opencode-vision-router", "options": { "model": "opencode-go/qwen3.7-plus" } }
  ]
}

Custom subagent name and cache directory

{
  "plugins": [
    {
      "package": "opencode-vision-router",
      "options": {
        "model": "anthropic/claude-3-5-haiku",
        "agent": "image-reader",
        "tmpDir": "/var/tmp/opencode-vision"
      }
    }
  ]
}

Force routing on a multimodal main model

If your main model can already see images, routing is skipped by default. Set force: true to always route — e.g. to send images to a cheaper vision model while keeping a stronger text model as main:

{
  "plugins": [
    {
      "package": "opencode-vision-router",
      "options": { "model": "openai/gpt-4o-mini", "force": true }
    }
  ]
}

Consuming the plugin

opencode-vision-router is an opencode plugin and is wired up only through your opencode.json (see Installation / Configuration above). Its helper functions (image.ts, transform.ts, agent.ts) are plain, dependency-free implementation details used by the plugin itself and covered by the test suite — they are intentionally not part of the package's public API, so import them from the source tree only if you are extending the plugin, not from the published package.

🖥️ Screenshots

A pasted image is intercepted and routed to the vision subagent:

Request flow:

sequenceDiagram
  participant U as User
  participant M as Main agent (text-only)
  participant V as vision subagent
  participant L as Vision LLM
  U->>M: paste image + question
  M->>M: image stripped → path pointer
  M->>V: Task(image path, question)
  V->>L: read(path) + analyze
  L-->>V: text analysis
  V-->>M: text analysis
  M-->>U: answer

🛠️ Development

bun install
bun test        # run the test suite
bunx tsc --noEmit   # type-check

Project structure

src/
  index.ts        # plugin entrypoint — dual V1/V2 default export (no public re-exports)
  types.ts        # shared option & message types
  image.ts        # resolveImagePath / resolveMediaPath, decodeDataUrl, extForMime
  transform.ts    # transformMessages / transformV2Messages, imagePointer (pure)
  agent.ts        # buildVisionAgentConfig, applyConfig, applyAgent, delegationInstruction
  index.test.ts   # Bun tests

🤝 Contributing

Contributions welcome! This is a small, single-purpose plugin, so the bar for patches is low.

  1. Fork the repo and create a branch: git checkout -b fix/my-change.
  2. Install deps and run the checks: bun install && bun test && bunx tsc --noEmit.
  3. Add tests for any new behavior.
  4. Keep the plugin self-contained — prefer extending the injected subagent over adding new files users must wire up.
  5. Open a PR with a clear description of the problem and the fix.

Please file issues for bugs, hook-contract changes in opencode, or model-compatibility reports.

🚀 Releasing

Publishing, OIDC/Trusted-Publisher setup, and the build→dist/ flow are documented in release.md.

📜 License

MIT © opencode-vision-router contributors.