opencode/packages/plugin/src/v2/effect
2026-07-12 21:21:07 -05:00
..
agent.ts refactor(schema): apply session review decisions (#35793) 2026-07-07 22:10:11 -04:00
aisdk.ts feat(plugin): add session request hook (#35794) 2026-07-07 19:56:47 -04:00
catalog.ts refactor(schema): apply session review decisions (#35793) 2026-07-07 22:10:11 -04:00
command.ts refactor(schema): apply session review decisions (#35793) 2026-07-07 22:10:11 -04:00
event.ts feat(plugin): add session request hook (#35794) 2026-07-07 19:56:47 -04:00
filesystem.ts feat(plugin): add v2 effect host (#33111) 2026-06-21 08:05:49 -04:00
index.ts feat(plugin): add session request hook (#35794) 2026-07-07 19:56:47 -04:00
integration.ts feat(plugin): add session request hook (#35794) 2026-07-07 19:56:47 -04:00
location.ts feat(plugin): add v2 effect host (#33111) 2026-06-21 08:05:49 -04:00
npm.ts feat(plugin): add v2 effect host (#33111) 2026-06-21 08:05:49 -04:00
path.ts feat(plugin): add v2 effect host (#33111) 2026-06-21 08:05:49 -04:00
PLAN.md fix(core): restore plugins after failed activation 2026-07-09 16:28:22 -04:00
plugin.ts feat(plugin): add session request hook (#35794) 2026-07-07 19:56:47 -04:00
README.md feat(plugin): add session request hook (#35794) 2026-07-07 19:56:47 -04:00
reference.ts feat(plugin): add session request hook (#35794) 2026-07-07 19:56:47 -04:00
registration.ts feat(plugin): add session request hook (#35794) 2026-07-07 19:56:47 -04:00
session.ts fix(plugin): restore package publishing 2026-07-09 22:18:58 -04:00
skill.ts refactor(schema): apply session review decisions (#35793) 2026-07-07 22:10:11 -04:00
tool.ts refactor(core): replace deferred tool option with codemode (#36560) 2026-07-12 21:21:07 -05:00

OpenCode V2 Effect Plugin API

The Effect plugin API grants plugins two in-process capabilities:

  • hook installs behavior at an OpenCode extension point.
  • reload reruns every transform hook for a stateful domain.

Defining A Plugin

import { Plugin } from "@opencode-ai/plugin/v2/effect"
import { Effect } from "effect"

export default Plugin.define({
  id: "example",
  effect: Effect.fn(function* (ctx) {
    yield* ctx.catalog.transform((catalog) => {
      catalog.provider.update("example", (provider) => {
        provider.name = "Example"
      })
    })
  }),
})

Plugin setup registers hooks imperatively through each domain's hook method.

Configuration supplied for the plugin is available as ctx.options.

Registrations are owned by the plugin scope. Closing the scope removes them automatically; a registration may also be removed early through dispose.

Transform Hooks

Transform hooks contribute to stateful domains:

yield *
  ctx.agent.transform((agent) => {
    agent.update("reviewer", (item) => {
      item.description = "Reviews code for regressions"
      item.mode = "subagent"
    })
  })

OpenCode rebuilds the domain when a transform is registered or disposed. A rebuild starts from fresh domain state and runs every active transform in registration order.

Available transform hooks are namespaced by domain:

ctx.agent.transform
ctx.catalog.transform
ctx.command.transform
ctx.integration.transform
ctx.reference.transform
ctx.skill.transform

Runtime Hooks

Runtime hooks intercept live operations rather than rebuilding domain state:

yield *
  ctx.aisdk.hook(
    "sdk",
    Effect.fn(function* (event) {
      if (event.package !== "@ai-sdk/xai") return
      const mod = yield* Effect.promise(() => import("@ai-sdk/xai"))
      event.sdk = mod.createXai(event.options)
    }),
  )

yield *
  ctx.aisdk.hook("language", (event) => {
    if (event.model.providerID !== "xai") return
    event.language = event.sdk.responses(event.model.api.id)
  })

Hooks run sequentially in registration order. Later hooks observe mutations made by earlier hooks.

Session request context is mutable immediately before provider dispatch:

yield *
  ctx.session.hook("request", (event) => {
    event.tools.read.description = "Read a file using narrow line ranges."
    delete event.tools.write
  })

Reloading A Domain

When data captured by a transform changes, reload the affected domain:

let data = yield * loadCatalog()

yield *
  ctx.catalog.transform((catalog) => {
    applyCatalog(data, catalog)
  })

data = yield * loadCatalog()
yield * ctx.catalog.reload()

Reload belongs to the domain, not an individual registration. ctx.catalog.reload() reruns every active catalog transform and publishes the rebuilt catalog.

Available reload operations are:

ctx.agent.reload()
ctx.catalog.reload()
ctx.command.reload()
ctx.integration.reload()
ctx.reference.reload()
ctx.skill.reload()