The Promise plugin API at @opencode/plugin is the async/await equivalent of @opencode/plugin/effect. It grants plugins the same two in-process capabilities:
hookinstalls behavior at an OpenCode extension point.reloadreruns every transform hook for a stateful domain.
The Promise API uses Promises instead of Effects for setup, runtime hook
callbacks, hook registration, reload, and Registration.dispose. Transform
editor callbacks remain synchronous.
import { Plugin } from "@opencode/plugin"
export default Plugin.define({
id: "example",
setup: async (ctx) => {
await ctx.provider.transform((editor) => {
editor.update("example", (provider) => {
provider.name = "Example"
})
})
},
})Plugin setup registers hooks imperatively through each domain's hook method.
It may return a synchronous or asynchronous cleanup function. OpenCode awaits
the cleanup when the plugin is unloaded or replaced:
setup: async (ctx) => {
const timer = setInterval(refresh, 60_000)
return () => clearInterval(timer)
}Configuration supplied for the plugin is available as ctx.options.
A registration may be removed early through dispose:
const registration = await ctx.model.transform(applyModelPolicy)
await registration.dispose()Transform hooks contribute to stateful domains. The editor is synchronous, so load asynchronous data before registering a transform or reloading its domain:
const description = await loadReviewerDescription()
await ctx.agent.transform((agent) => {
agent.update("reviewer", (item) => {
item.description = description
item.mode = "subagent"
})
})Available transform hooks are namespaced by domain:
ctx.agent.transform
ctx.command.transform
ctx.integration.transform
ctx.mcp.transform
ctx.model.transform
ctx.provider.transform
ctx.reference.transform
ctx.skill.transform
ctx.tool.transform
ctx.vcs.transform
ctx.websearch.transformProvider transforms contribute provider settings and immutable model definitions. After provider availability is resolved,
model transforms edit the complete active-provider candidate collection in order. Use ctx.model.transform for runtime
model restrictions; editor.provider.get() reads source templates even when their provider is inactive.
await ctx.model.transform((editor) => {
editor
.list()
.filter((model) => model.cost.some((tier) => tier.output > 20))
.forEach((model) => {
editor.remove(model.providerID, model.id)
})
})Runtime hooks intercept live operations:
await ctx.aisdk.hook("sdk", async (event) => {
if (event.package !== "@ai-sdk/xai") return
const mod = await import("@ai-sdk/xai")
event.sdk = mod.createXai(event.options)
})
await ctx.aisdk.hook("language", (event) => {
if (event.model.providerID !== "xai") return
event.language = event.sdk.responses(event.model.modelID)
})Session context is mutable immediately before provider dispatch:
await ctx.session.hook("context", (event) => {
event.tools.read.description = "Read a file using narrow line ranges."
delete event.tools.write
})
await ctx.session.hook("retry", (event) => {
if (event.attempt >= 3) event.decision = { retry: false }
})Promise tools use complete executable tool values with async executors:
import { Schema } from "effect"
await ctx.tool.transform((tools) => {
tools.add({
name: "echo",
options: { codemode: false },
description: "Echo text",
input: Schema.Struct({ text: Schema.String }),
output: Schema.Struct({ text: Schema.String }),
execute: async ({ text }) => ({ output: { text }, content: text }),
})
})When data captured by a transform changes, reload the affected domain:
const source = { providers: await loadProviders() }
await ctx.provider.transform((editor) => {
source.providers.forEach((provider) => editor.add(provider))
})
source.providers = await loadProviders()
await ctx.provider.reload()loadProviders() returns entries shaped as { info: Provider.Info, models: readonly Model.Info[] }. Provider reloads
also invalidate the active model result, so every model transform runs again with the refreshed definitions. Model
callbacks edit raw overrides; provider defaults are merged once when the result is committed.
Available reload operations are:
ctx.agent.reload()
ctx.command.reload()
ctx.integration.reload()
ctx.mcp.reload()
ctx.model.reload()
ctx.provider.reload()
ctx.reference.reload()
ctx.skill.reload()
ctx.tool.reload()
ctx.vcs.reload()
ctx.websearch.reload()