OpenCode V2 Development
Use OpenCode V2 and its Effect-native APIs unless the user explicitly asks for
V1, a Promise-only integration, or hybrid compatibility.
Source Rule
- Read the relevant current V2 guide before writing code:
- Check the target project's exact pinned OpenCode and Effect package versions.
Inspect both published metadata and the installed
package.json exports
map before using a subpath. Confirm that every OpenCode package resolves to
a mutually compatible release and that Effect platform packages use the
same Effect release channel.
- Treat moving tags in installation examples as discovery inputs, not package
constraints. Resolve and lock exact versions. Do not assume separately
moving plugin, client, and SDK tags form a compatible set, or replace a
missing Effect export with a Promise/root entrypoint.
- Do not infer V2 contracts from V1 docs or from
https://opencode.ai/config.json.
Defaults
- Write plugins with
@opencode-ai/plugin/effect, Plugin.define, and an
effect function. Use the plugin Scope for registrations, finalizers, and
scoped fibers instead of manual Promise cleanup.
- Connect applications with
@opencode-ai/client/effect. Compose operations
with Effect, consume subscriptions as Stream values, and use
@opencode-ai/client/effect/service for the Node background service.
- Embed OpenCode with
@opencode-ai/sdk/effect. Own the host with
Effect.scoped or provide it through OpenCode.layer().
- Use the generated client instead of hand-written HTTP calls when an endpoint
is available. For direct API work, verify the operation and schema in the V2
API reference or the running server's
/openapi.json, use Effect's
HttpClient, and decode request and response boundaries with Schema.
- Keep decoded branded values such as
AbsolutePath, Location.Ref, and
resource IDs. Do not cast plain strings or untrusted responses into them.
- Keep expected client failures in the Effect error channel. Use
Effect.orDie
only when the surrounding application deliberately treats that failure as a
defect.
Plugin Example
import { Plugin } from "@opencode-ai/plugin/effect"
import { Effect } from "effect"
export default Plugin.define({
id: "example",
effect: (ctx) =>
Effect.gen(function* () {
yield* ctx.storage.set("loaded", true)
yield* Effect.addFinalizer(() => ctx.storage.remove("loaded"))
}),
})
Plugin hook callbacks and registered command, tool, integration, or websearch
executors return Effect values. Tool inputs and outputs use Effect Schema
rather than unchecked JSON Schema casts.
Client Example
import { AbsolutePath, Location, OpenCode } from "@opencode-ai/client/effect"
import { Effect } from "effect"
import { FetchHttpClient } from "effect/unstable/http"
const program = Effect.gen(function* () {
const client = yield* OpenCode.make({ baseUrl: "http://localhost:4096" })
return yield* client.session.create({
location: Location.Ref.make({
directory: AbsolutePath.make("/workspace"),
}),
})
})
const session = await Effect.runPromise(
program.pipe(Effect.provide(FetchHttpClient.layer)),
)
Use Stream operators for client.event.subscribe() and other streaming
operations. Provide the required HTTP, filesystem, and platform layers at the
application boundary, not inside reusable workflows.
SDK Example
import { AbsolutePath, Location, OpenCode } from "@opencode-ai/sdk/effect"
import { Effect } from "effect"
const program = Effect.scoped(
Effect.gen(function* () {
const opencode = yield* OpenCode.create()
return yield* opencode.sessions.create({
location: Location.Ref.make({
directory: AbsolutePath.make("/workspace"),
}),
})
}),
)
const session = await Effect.runPromise(program)
Promise And V1 Exceptions
- Retain Promise plugin, client, or SDK code only when documenting migration
input, maintaining a concrete Promise-only consumer, or providing an
explicitly labelled hybrid compatibility path. Keep the Effect result as the
recommended V2 output.
- Use
@opencode-ai/client, @opencode-ai/client/service, or the Promise SDK
only inside that documented exception. Do not present them as the default V2
starting point.
- Treat V1 plugin and server APIs as migration inputs only. V1 plugins do not
run on V2, and V1 server integrations must move to the V2 API.
- Preserve supported V1 configuration only when compatibility is the goal;
new native V2 configuration and code should use V2 shapes and entrypoints.
Verification
Typecheck examples against the target project's exact locked package set after
verifying every imported subpath in published and installed package metadata.
Exercise plugin load and cleanup, and run one real client or SDK operation. For
direct HTTP work, verify the same operation with opencode2 api or the
Effect-native generated client.
1---2name: opencode-effect3description: Develop and migrate OpenCode V2 plugins, clients, SDK hosts, and HTTP API integrations. Use for the OpenCode plugin API, `@opencode-ai/client`, `@opencode-ai/sdk`, server API, Effect entrypoints, or V1-to-V2 API migration.4license: Apache-2.05---67# OpenCode V2 Development89Use OpenCode V2 and its Effect-native APIs unless the user explicitly asks for10V1, a Promise-only integration, or hybrid compatibility.1112## Source Rule13141. Read the relevant current V2 guide before writing code:15 - plugins: <https://opencode.ai/v2/docs/build/plugins/effect>16 - network client: <https://opencode.ai/v2/docs/build/client/effect>17 - embedded SDK: <https://opencode.ai/v2/docs/build/sdk/effect>18 - HTTP API: <https://opencode.ai/v2/docs/api>19 - V1 migration inputs: <https://opencode.ai/v2/docs/migrate-v1>202. Check the target project's exact pinned OpenCode and Effect package versions.21 Inspect both published metadata and the installed `package.json` `exports`22 map before using a subpath. Confirm that every OpenCode package resolves to23 a mutually compatible release and that Effect platform packages use the24 same Effect release channel.253. Treat moving tags in installation examples as discovery inputs, not package26 constraints. Resolve and lock exact versions. Do not assume separately27 moving plugin, client, and SDK tags form a compatible set, or replace a28 missing Effect export with a Promise/root entrypoint.294. Do not infer V2 contracts from V1 docs or from30 `https://opencode.ai/config.json`.3132## Defaults3334- Write plugins with `@opencode-ai/plugin/effect`, `Plugin.define`, and an35 `effect` function. Use the plugin `Scope` for registrations, finalizers, and36 scoped fibers instead of manual Promise cleanup.37- Connect applications with `@opencode-ai/client/effect`. Compose operations38 with `Effect`, consume subscriptions as `Stream` values, and use39 `@opencode-ai/client/effect/service` for the Node background service.40- Embed OpenCode with `@opencode-ai/sdk/effect`. Own the host with41 `Effect.scoped` or provide it through `OpenCode.layer()`.42- Use the generated client instead of hand-written HTTP calls when an endpoint43 is available. For direct API work, verify the operation and schema in the V244 API reference or the running server's `/openapi.json`, use Effect's45 `HttpClient`, and decode request and response boundaries with `Schema`.46- Keep decoded branded values such as `AbsolutePath`, `Location.Ref`, and47 resource IDs. Do not cast plain strings or untrusted responses into them.48- Keep expected client failures in the Effect error channel. Use `Effect.orDie`49 only when the surrounding application deliberately treats that failure as a50 defect.5152## Plugin Example5354```ts55import { Plugin } from "@opencode-ai/plugin/effect"56import { Effect } from "effect"5758export default Plugin.define({59 id: "example",60 effect: (ctx) =>61 Effect.gen(function* () {62 yield* ctx.storage.set("loaded", true)63 yield* Effect.addFinalizer(() => ctx.storage.remove("loaded"))64 }),65})66```6768Plugin hook callbacks and registered command, tool, integration, or websearch69executors return `Effect` values. Tool inputs and outputs use Effect `Schema`70rather than unchecked JSON Schema casts.7172## Client Example7374```ts75import { AbsolutePath, Location, OpenCode } from "@opencode-ai/client/effect"76import { Effect } from "effect"77import { FetchHttpClient } from "effect/unstable/http"7879const program = Effect.gen(function* () {80 const client = yield* OpenCode.make({ baseUrl: "http://localhost:4096" })81 return yield* client.session.create({82 location: Location.Ref.make({83 directory: AbsolutePath.make("/workspace"),84 }),85 })86})8788const session = await Effect.runPromise(89 program.pipe(Effect.provide(FetchHttpClient.layer)),90)91```9293Use `Stream` operators for `client.event.subscribe()` and other streaming94operations. Provide the required HTTP, filesystem, and platform layers at the95application boundary, not inside reusable workflows.9697## SDK Example9899```ts100import { AbsolutePath, Location, OpenCode } from "@opencode-ai/sdk/effect"101import { Effect } from "effect"102103const program = Effect.scoped(104 Effect.gen(function* () {105 const opencode = yield* OpenCode.create()106 return yield* opencode.sessions.create({107 location: Location.Ref.make({108 directory: AbsolutePath.make("/workspace"),109 }),110 })111 }),112)113114const session = await Effect.runPromise(program)115```116117## Promise And V1 Exceptions118119- Retain Promise plugin, client, or SDK code only when documenting migration120 input, maintaining a concrete Promise-only consumer, or providing an121 explicitly labelled hybrid compatibility path. Keep the Effect result as the122 recommended V2 output.123- Use `@opencode-ai/client`, `@opencode-ai/client/service`, or the Promise SDK124 only inside that documented exception. Do not present them as the default V2125 starting point.126- Treat V1 plugin and server APIs as migration inputs only. V1 plugins do not127 run on V2, and V1 server integrations must move to the V2 API.128- Preserve supported V1 configuration only when compatibility is the goal;129 new native V2 configuration and code should use V2 shapes and entrypoints.130131## Verification132133Typecheck examples against the target project's exact locked package set after134verifying every imported subpath in published and installed package metadata.135Exercise plugin load and cleanup, and run one real client or SDK operation. For136direct HTTP work, verify the same operation with `opencode2 api` or the137Effect-native generated client.