opencode
opencode copied to clipboard
feat(plugin)!: migrate plugin system to SDK v2
Summary
- Migrates plugin system to use SDK v2 types and client exclusively
- Removes v1 SDK compatibility layer - this is now a full breaking change
- Key context: Many hooks already had type drift between v1 contract and v2 runtime, so this breaking change is less severe than it appears
Why This Breaking Change Is Acceptable
Hook Types Were Already Broken
Analysis revealed significant type drift between what the plugin package defined and what the OpenCode core actually passed at runtime:
| Hook | Plugin Expected | Core Passed | Impact |
|---|---|---|---|
permission.ask |
Permission with title |
Permission.Info with message |
Field name mismatch - input.title returns undefined |
chat.params |
Provider |
Promise<Provider.Info> |
Promise wrapper not in type |
chat.message |
UserMessage |
MessageV2.Info |
Different type structure |
The core was already using @ts-expect-error workarounds to suppress these mismatches. Plugins accessing input.title in permission.ask were already getting undefined at runtime.
This Change Fixes the Type Mismatch
Instead of maintaining broken v1 types, we:
- Update the plugin contract to match the actual v2 runtime behavior
- Provide clean, consistent types that match what OpenCode core actually passes
- Remove the
UnifiedClientcomplexity (no more.v2accessor needed)
Changes
Plugin Package (packages/plugin)
- Updated all imports from
@opencode-ai/sdk→@opencode-ai/sdk/v2 - Changed hook types to use v2 SDK types (
PermissionRequestinstead ofPermission, etc.) - Simplified
PluginInput.clientto be v2OpencodeClientonly - Added
ToolContext.metadata()andToolResulttypes
Plugin Loader (packages/opencode)
- Removed v1 SDK client creation
- Removed unified client proxy (
.v2accessor) - Now creates and passes v2 SDK client only
Breaking Changes
| What | Before | After |
|---|---|---|
| SDK client | input.client (v1) + input.client.v2 (v2) |
input.client (v2 only) |
| Permission type | Permission with title |
PermissionRequest with permission |
| Permission reply | status: ask/deny/allow |
reply: once/always/reject |
| SDK calls | Nested { path, body, query } |
Flattened parameters |
Migration for Plugin Authors
- Change imports:
@opencode-ai/plugin(no/v2) - Update permission hook:
// Before (input: Permission, output: { status: "ask" | "deny" | "allow" }) => { ... } // After (input: PermissionRequest, output: { reply: "once" | "always" | "reject" }) => { ... } - Update SDK calls to use flattened parameters
Why Not Maintain v1 Compatibility?
- The SDK client had massive breaking changes (every method call changed from nested to flat)
- Hook types already had significant drift (not just style changes, but actual field mismatches)
- Maintaining both v1 and v2 code paths added complexity
- Clean break forces plugin authors to update, resulting in better type safety
Closes #7641