Skip to content

opencode 2.2.2.0 config definition change/break #6148

Closed

Description

@citizenadam

Summary

@vectorize-io/hindsight-coding-agents 0.8.2 is reported as failed by opencode 2.2.x when the plugin is registered as a directory path (install opencode does exactly that):

Server plugin error
Plugin: ~/.hindsight/coding-agents
Status: failed
Runtime: server
Source: ~/.hindsight/coding-agents
Error: Plugin must export a default definition with an id and an effect or setup function.
Reference: err_83624a09

The server log shows which module opencode actually loaded, and why it rejected it:

msg="loading plugin" id=/home/adamadmin/.hindsight/coding-agents entrypoint=file:///home/adamadmin/.hindsight/coding-agents/dist/index.js
message="failed to load plugin" target=/home/adamadmin/.hindsight/coding-agents cause="Cause([Fail(PluginModule.LoadError: Plugin must export a default definition with an id and an effect or setup function. (cause: SchemaError(Expected object\n  at [\"default\"])))])"

Root cause

opencode v2 resolves a plugin that is registered as a directory path through that directory's package.json exports map, preferring the ./server subpath export and otherwise falling back to ".". It does not fall back to <dir>/index.js once a manifest exists.

coding-agents/index.js is your v2 entrypoint and its own header documents the assumption that broke:

v1 follows package.json main (→ dist/index.js, the v1 plugin), while v2 ignores main and loads <dir>/index.js

In 2.2.x that is no longer true. package.json has exports["."].default = "./dist/index.js", i.e. the v1 plugin, whose default export is a function (input, options) => Hooks. opencode's v2 plugin definition schema requires an object ({ id, effect } or { id, setup }), so validation fails with Expected object at ["default"].

How the resolution rule was confirmed

Controlled experiment with a minimal plugin package registered by absolute path:

{"name":"fake-plugin","version":"1.0.0","type":"module",
 "main":"./a-main.js",
 "exports":{".":{"default":"./b-exports.js"},"./server":"./c-server.js"}}
  • a-main.js → export default function V1Plugin(ctx) { return {} }
  • b-exports.js → export default { id: "fake-exports", setup() {} }
  • c-server.js → export default { id: "fake-server", setup() {} }

Observed:

package.json exports entrypoint opencode 2.2.0 loaded
"." + "./server" file:///tmp/.../fake/c-server.js
"." only file:///tmp/.../fake/b-exports.js
main only, no exports <dir>/index.js

main was never used while exports was present. The same rule explains why @cortexkit/aft-opencode (which ships exports["./server"] = "./dist/entry/server.js") loads correctly on the same machine.

Fix

Expose the v2 entrypoint as the ./server subpath export, leaving "." (the v1 entry) untouched:

"exports": {
  ".": {
    "types": "./dist/index.d.ts",
    "default": "./dist/index.js"
  },
  "./server": "./index.js",
  "./dsh": "./dist/dsh.js",
  "./cordis.patch.yml": "./cordis.patch.yml",
  "./package.json": "./package.json",
  "./dist/*": "./dist/*"
}

With this single added line, a fresh opencode 2.2.0 process resolves:

msg="loading plugin" id=~/.hindsight/coding-agents entrypoint=file:///.../coding-agents/index.js

and the plugin then runs normally — from its own diagnostics log:

{"event":"skill_registered","id":"hindsight-coding-agent"}
{"event":"session_start","bank":"opencode::trade-schwab","pages":7}
{"event":"pages_ok","count":7}
{"event":"retain_ok","turns":1,"trigger":"idle"}

No load failure appears in the server log. main remains dist/index.js, so v1 hosts are unaffected by the change (they read main, not exports).

Note for anyone applying this by hand: a long-lived opencode server that already resolved this path keeps the old entrypoint cached and will still report the failure until it is restarted; a new process picks up the fixed manifest immediately.

Second, related issue: the installer registers the plugin twice

hindsight-coding-agents install opencode writes the same path under both the v2 key plugins and the legacy key plugin. opencode 2.2 normalizes the legacy key into plugins, so opencode debug config reports the target twice in one document:

"plugins": [
  "/home/adamadmin/.hindsight/coding-agents",
  "@cortexkit/[email protected]",
  "@cortexkit/[email protected]",
  "/home/adamadmin/.hindsight/coding-agents",
  "/opencode/opencode-beads"
]

The supervisor dedupes by target string, so this does not raise Duplicate plugin ID, but the target is resolved and loaded on every activation cycle redundantly. Dropping the legacy plugin array (2.x-only hosts) leaves a single plugins entry.

Environment

  • opencode v2.2.0 (opencode --version), Linux x64; the desktop UI reports the build as 2.2.2.0
  • @vectorize-io/hindsight-coding-agents 0.8.2, installed via npx @vectorize-io/hindsight-coding-agents install opencode
  • plugin registered as ~/.hindsight/coding-agents under plugins in ~/.config/opencode/opencode.jsonc

AI disclosure

This report was written by an AI coding agent (OpenCode, model deepseek-v4.1-flash) operating on the reporter's machine, under their direction. The root-cause analysis, the controlled resolution experiment, the one-line manifest patch, and the verification run were all performed by that agent. Log excerpts are verbatim from the reporter's machine; paths are the reporter's home directory.

Activity

  1. nicoloboschi commented on Oct 29, 2026

    @nicoloboschi
    Collaborator

    Fixed in @vectorize-io/hindsight-coding-agents 0.8.3. It adds exports["./server"] → ./index.js, which OpenCode 2.2 reads first when it resolves the plugin folder, so it loads the v2 entry. I checked it on a real OpenCode 2.2.1: the plugin loads index.js, adds memory to the prompt and saves the session. OpenCode 1.18.9 still loads the v1 plugin. To update: npx @vectorize-io/hindsight-coding-agents@latest install opencode2, or wait for auto-update.

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions