Skip to content

Add OpenCode v2 support (port to @opencode/plugin / Plugin.define API) #5

Description

@morpheus9393

Why

OpenCode v2 (2.0.x) hard-breaks the plugin API: plugins must now be defined with Plugin.define({ id, setup(ctx) }) from the new @opencode/plugin package. v1-style function-export plugins built on @opencode-ai/plugin no longer load under v2. This plugin currently targets v1 (recent fixes #3 / PR #4 target 1.18.x), which makes it unusable on opencode 2.x installations (e.g. 2.0.15).

v1 API surface used by this plugin today

  • plugins/vision-helper.ts: v1 function-export plugin (export default (async () => ({...})) satisfies Plugin) with hooks tool.definition, tool.execute.before, experimental.chat.system.transform, experimental.chat.messages.transform
  • tools/vision.ts: v1 tool() helper from @opencode-ai/plugin with Zod tool.schema.* args
  • Bun globals (Bun.file / Bun.write) — PR fix: replace Bun globals with Node fs APIs for OpenCode 1.18 compatibility #4 already proposes replacing them with Node fs
  • Ships as loose .ts files for config-dir auto-discovery (no @opencode-ai/plugin dependency in package.json)

Port checklist (v2)

  1. Plugin shape: import from @opencode/plugin and wrap in Plugin.define({ id: "opencode-vision", async setup(ctx) { ... } }). v2 still auto-loads .opencode/plugins/*.ts; the config key is now plugins (v1 plugin is gone). The plugin id is required.
  2. tool.definition → removed in v2: use ctx.tool.transform((editor) => editor.update("vision", (t) => { t.description = ... })).
  3. tool.execute.before → ctx.tool.hook("execute.before", (event) => ...): same concept; v2 event is { tool, sessionID, agent, messageID, id, input }; mutate event.input (the native-vision block args go here).
  4. Both experimental.chat.*.transform hooks → ctx.session.hook("context", ...): edit event.system (SystemParts) / event.messages. Fires per model request — the existing "clean injected hints each pass" discipline becomes mandatory. The native-multimodal check moves here (event.model: Model.Ref).
  5. Image part reads: v1 base64 FilePart.url → v2 MediaPart (media.source.data, media.source.mediaType, part.filename). The compaction skip via msg.info.summary needs the v2 equivalent from event/message metadata.
  6. vision custom tool: Zod args → JSON-Schema input; execute(input, context) returns { content }; register via ctx.tool.transform((editor) => editor.add({ name: "vision", ... })). context.signal / context.progress are available for the external VLM call.
  7. Bun globals → Node fs (building on PR fix: replace Bun globals with Node fs APIs for OpenCode 1.18 compatibility #4).
  8. Subagent mode: good news — the shipped agents/image-reader.md file is exactly the v2-compatible pattern. v2 plugins cannot define agents in code anymore (AgentEditor has no add), so file-based agents remain the way to go; the installer just needs to keep placing the file where v2 discovers it.
  9. Install script (bin/install.js): update for v2 discovery paths, the plugins config key, and add the @opencode/plugin dependency.

No structural blockers

Every hook used by this plugin maps onto an existing v2 surface (tool.definition via the transform editor, the rest directly) — the port is mostly mechanical.

Reference implementations already on v2

Happy to help test a v2 port against opencode 2.0.15.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions