Skip to content

feat(opencode): add OpenCode v2 plugin adapter and setup opencode-v2 command #1220

Description

@ScorpionConMate

🔍 Problem Description

OpenCode 2.x introduced a new plugin API. The OpenCode adapter installed by engram setup opencode targets the 1.x plugin API, so on OpenCode 2.x it fails to load at startup and every plugin feature is silently inactive:

level=WARN message="failed to load plugin" target=~/.config/opencode/plugins/engram.ts
cause="PluginModule.LoadError: Plugin must export a default definition with an id and an effect or setup function.
(cause: SchemaError(Missing key at [\"default\"]))"

OpenCode's migration guide is explicit: "V1 plugin implementations do not run in V2." A V2 plugin must export default Plugin.define({ id, setup }) (from @opencode/plugin) instead of exporting an async function from @opencode-ai/plugin. The current adapter does export const Engram: Plugin = async (ctx) => ({ event, "chat.message", "tool.execute.after", "experimental.chat.system.transform", "experimental.session.compacting" }), so the V2 loader finds no default export and rejects the module.

Impact on OpenCode 2.x: no Memory Protocol injection, no prompt capture, no session registration/tracking, no save nudges, no compaction context, no passive capture. The MCP server still connects (engram mcp), so manual memory tools keep working — which is why the regression is quiet.

Reproduced on opencode 2.0.4 (stable channel) / engram 1.20.0, at every server start and for every project location.

💡 Proposed Solution

Add a V2 adapter and a dedicated setup command, leaving the 1.x path untouched:

  1. New adapter at plugin/opencode-v2/engram.ts, embedded through internal/setup/plugins/opencode-v2/ (same go:embed pattern as the current one).
  2. New agent entry: engram setup opencode-v2, installed to the same destination ~/.config/opencode/plugins/engram.ts — OpenCode auto-discovers that path in both 1.x and 2.x. MCP registration needs a V2-aware injector: V2 config uses mcp.servers.<name> with disabled: false, while the current injectOpenCodeMCP() writes the 1.x shape (mcp.<name> with enabled: true).
  3. engram setup opencode stays as-is for 1.x. Nice-to-have: each command detects the installed OpenCode version (opencode --version) and hints at the other command when it does not match.
  4. Port the hooks to the V2 domain API:
1.x V2
export const Engram: Plugin = async (ctx) => ... export default Plugin.define({ id: "engram", async setup(ctx) { ... } })
event ctx.event.subscribe() (cleanup via AbortController)
chat.message ctx.session.hook("prompt", ...) — text comes from event.prompt.text
tool.execute.after ctx.tool.hook("execute.after", ...)
experimental.chat.system.transform ctx.session.hook("context", ...) — append to event.system
experimental.session.compacting ctx.session.hook("compaction", ...) — exact injection point to validate
ctx.directory ctx.location.directory
  1. Avoid Bun globals in the V2 adapter (Bun.spawn, Bun.spawnSync, Bun.file, Bun.which) — use node:child_process / node:fs instead. See Plugin assumes a Bun runtime, but opencode's plugin host has no Bun global (plugin is silently inert) #1218: OpenCode's plugin host has no Bun global, so a Bun-based adapter would load but stay inert.
  2. Add @opencode/plugin (matching the target OpenCode release, 2.0.4 today) as the adapter's type dependency.

Behavior parity to preserve: ensureSession(), prompt capture (truncate 2000, <private> stripping), ENGRAM_TOOLS filtering, Memory Protocol appended to the last system entry (single-system-block compatibility), nudge debounce, compaction context, passive capture, ENGRAM_PORT / ENGRAM_BIN / ENGRAM_NUDGE_COOLDOWN_SECS overrides, sub-agent session suppression (#116), and the ENGRAM_BIN absolute-path patch applied by the installer.

📦 Affected Area

CLI (commands, flags)

🔄 Alternatives Considered

  • One adapter supporting both APIs: OpenCode 1.18.29+ accepts a default export with both setup() (V2) and server() (V1), but the two hook APIs are different domains and do not translate automatically — both implementations would still be needed in one file, and OpenCode < 1.18.29 would stop working. Two files + two commands is clearer and keeps the 1.x path frozen.
  • Auto-detect inside engram setup opencode: no new command, but it silently changes the installed adapter for everyone; a dedicated command is more explicit and lets the V2 adapter evolve at its own pace.
  • Do nothing: not viable; OpenCode 2.x is the current stable line and the V1 adapter already fails on every start.

📎 Additional Context

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions