Summary
The plugin does not load on OpenCode 2.x. It still ships a V1-only entrypoint, so OpenCode's plugin loader rejects it before setup ever runs.
Error
PluginModule.LoadError: Plugin must export a default definition with an id and an effect or setup function.
(cause: SchemaError(Expected object at ["default"])))
Logged as level=WARN message="failed to load plugin" target=opencode-auto-resume on every load, in both opencode run and the desktop/background service.
Environment
- OpenCode
2.0.12 / 2.0.14 (desktop + CLI), config key is plugins
opencode-auto-resume@1.1.18
- Reproduced by adding it with
opencode plugin add opencode-auto-resume and starting any session
Why
V2 requires a default-exported object with an id and a setup(ctx) (or effect(ctx)). The published entrypoint has no default export matching that shape, so the schema check fails during module load.
Migration reference: https://opencode.ai/v2/docs/build/plugins/migrate-v1
The dual-entrypoint escape hatch documented upstream would keep V1 working while adding V2:
export default {
...Plugin.define({
id: "auto-resume",
async setup(ctx) {
// V2 hooks
},
}),
async server() {
return {
// existing V1 hooks
}
},
}
Port scope (heads-up, this is the non-obvious part)
Beyond renaming the entrypoint, several V1 APIs the plugin relies on are not exposed on the V2 plugin context, so a straight server() → setup() copy will not be enough:
Missing on ctx (verified by logging Object.keys(ctx) and Object.keys(ctx.session) in a V2 plugin):
client.session.list, session.status, session.abort, session.summarize, session.todo, session.messages
client.app.log
client.provider, client.config, client.tool.ids
client.tui (TUI is a separate plugin surface in V2)
V2 ctx.session only exposes: hook, create, get, switchAgent, switchModel, prompt, generate, command, synthetic, interrupt, update, move, wait, context.
Hooks with no direct V2 equivalent:
chat.message → closest is ctx.session.hook("prompt", ...) (runs at prompt admission, not per message)
command.execute.before → docs say to use a command transform or the prompt hook depending on intent
tool.execute.before → ctx.tool.hook("execute.before", ...) (note: args arrive as event.input, and the shell tool is now named shell, not bash)
tool.execute.after → ctx.tool.hook("execute.after", ...)
Happy to test a next/V2 build if you want a hand verifying it.
Workaround currently used
None — the plugin has to be removed from plugins to keep the log clean.
Summary
The plugin does not load on OpenCode 2.x. It still ships a V1-only entrypoint, so OpenCode's plugin loader rejects it before
setupever runs.Error
Logged as
level=WARN message="failed to load plugin" target=opencode-auto-resumeon every load, in bothopencode runand the desktop/background service.Environment
2.0.12/2.0.14(desktop + CLI), config key ispluginsopencode-auto-resume@1.1.18opencode plugin add opencode-auto-resumeand starting any sessionWhy
V2 requires a default-exported object with an
idand asetup(ctx)(oreffect(ctx)). The published entrypoint has nodefaultexport matching that shape, so the schema check fails during module load.Migration reference: https://opencode.ai/v2/docs/build/plugins/migrate-v1
The dual-entrypoint escape hatch documented upstream would keep V1 working while adding V2:
Port scope (heads-up, this is the non-obvious part)
Beyond renaming the entrypoint, several V1 APIs the plugin relies on are not exposed on the V2 plugin context, so a straight
server()→setup()copy will not be enough:Missing on
ctx(verified by loggingObject.keys(ctx)andObject.keys(ctx.session)in a V2 plugin):client.session.list,session.status,session.abort,session.summarize,session.todo,session.messagesclient.app.logclient.provider,client.config,client.tool.idsclient.tui(TUI is a separate plugin surface in V2)V2
ctx.sessiononly exposes:hook, create, get, switchAgent, switchModel, prompt, generate, command, synthetic, interrupt, update, move, wait, context.Hooks with no direct V2 equivalent:
chat.message→ closest isctx.session.hook("prompt", ...)(runs at prompt admission, not per message)command.execute.before→ docs say to use a command transform or the prompt hook depending on intenttool.execute.before→ctx.tool.hook("execute.before", ...)(note: args arrive asevent.input, and the shell tool is now namedshell, notbash)tool.execute.after→ctx.tool.hook("execute.after", ...)Happy to test a
next/V2 build if you want a hand verifying it.Workaround currently used
None — the plugin has to be removed from
pluginsto keep the log clean.