Skip to content

[Bug] OpenCode V2 integration broken: plugin ships the V1 plugin API and both DB readers target the V1 schema #654

Closed

Description

@kristhianmanue1

中文摘要(TL;DR)

OpenCode V2 下 Memmy 集成完全失效,存在两个根因:

  1. 插件仍使用 OpenCode V1 插件 API:memmy-opencode-plugin.ts 模板导入 @opencode-ai/plugin 并导出 V1 钩子函数;OpenCode V2 已改为 { id, setup(ctx) } 定义(@opencode/plugin),官方迁移指南明确 "V1 plugin implementations do not run in V2",因此插件加载直接失败:Cannot find package '@opencode-ai/plugin'。
  2. 两处数据库读取器仍按 V1 表结构:插件捕获路径(readOpencodeHookSourceTurn)与桌面历史扫描(Memory/src/agent-source/adapters/opencode/db-reader.ts)都要求 session/message/part 三张表;OpenCode V2 已迁移为 session_v2 + session_message(消息内容内联在 data.content[]),因此扫描器会静默忽略整个 OpenCode V2 数据库。

后果:无自动召回注入、无回合捕获、无 memmy_memory_* 工具、/memmy-resume 失效(记忆服务本身健康)。

下方 gist 是已验证的社区 PoC,在 OpenCode V2 + Memory 2.1.4 上完整恢复了上述功能。修复思路:V2 插件模板 + 兼容 session_v2 的读取器,可同时修复插件与桌面扫描两条链路。如有需要我可以直接提 PR。


OpenCode V2 integration is broken: plugin ships the V1 plugin API and both DB readers target the V1 schema

Environment

  • Memmy desktop 1.2.x with Memory service 2.1.4 (macOS arm64, local mode)
  • OpenCode V2 (current stable, client-server architecture)
  • Integration installed via the desktop app (opencode agent source → plugin + skill + command)

Symptom

OpenCode V2 refuses to load the installed plugin on every start:

Plugin failed: /Users/<user>/.config/opencode/plugins/memmy-memory.js
Run /plugins to view details.

Server log (~/.local/share/opencode/log/opencode.log):

level=WARN message="failed to load plugin" target=.../plugins/memmy-memory.js
cause="Cause([Die(ResolveMessage: Cannot find package '@opencode-ai/plugin'
imported from /Users/<user>/.config/opencode/plugins/memmy-memory.js)])"

Consequences: no automatic recall injection, no turn capture, no memmy_memory_* tools, and /memmy-resume does nothing. The memory service itself is healthy (/api/v1/health → ok).

Root cause 1 — the plugin template uses the OpenCode V1 plugin API

Memory/src/agent-source/integration/templates/memmy-opencode-plugin.ts (and the copy under App/backend/src/adapters/outbound/skill-writer/templates/) renders a plugin that:

  • imports { tool } from "@opencode-ai/plugin" (the V1 SDK package), and
  • exports a V1-style async function returning the V1 hooks map (chat.message, tool.execute.before, tool.execute.after, experimental.text.complete, event, dispose).

OpenCode V2 intentionally broke the plugin API. From the official V1→V2 migration guide (https://opencode.ai/v2/docs/build/plugins/migrate-v1):

V1 plugin implementations do not run in V2. Moving a file or renaming its config entry is not enough.

A V2 plugin must default-export a definition (id + setup(ctx)) built on @opencode/plugin, register tools through ctx.tool.transform, use ctx.session.hook("prompt", ...), and subscribe to events via ctx.event.subscribe(). Installing the old @opencode-ai/plugin package does not fix anything because the entrypoint shape itself is no longer invoked.

Root cause 2 — both OpenCode DB readers target the V1 schema, V2 renamed the tables

OpenCode V2 also migrated its SQLite database. V1 tables session, message, part are gone; V2 stores:

  • session_v2 (same useful columns: id, parent_id, directory, agent, revert, plus project_id, time_*)
  • session_message (id, session_id, type = user|assistant|system|synthetic|idle|model-switched, seq, data) with message parts inline in data.content[] ({type:"text"|"tool"|"reasoning", ...}; tool state carries {status, input, output})

Two code paths require the old tables:

  1. Plugin capture path — readOpencodeHookSourceTurn in the bridge prepares SELECT ... FROM session/message/part, so every session.idle capture fails with "no such table".
  2. Desktop historical scan — Memory/src/agent-source/adapters/opencode/db-reader.ts:
function createSource(db) {
  if (!hasTable(db, "session") || !hasTable(db, "message") || !hasTable(db, "part")) return null;

so the scanner silently ignores an OpenCode V2 database entirely — no episodes, no L1, nothing from OpenCode.

Verified working proof of concept

To confirm the service side is fine and only the integration layer is stale, I wrote a community V2 port that:

  • keeps memmy-workspace-bridge.mjs unchanged (runtime session, startRuntimeTurn, completeSourceTurn, L3),
  • registers memmy_memory_search/get/add via ctx.tool.transform,
  • injects the recall packet through ctx.session.hook("prompt", ...) (persisted text is later unwrapped via the existing <current_user_request> convention, so unwrapUserRequest semantics are preserved),
  • captures turns on session.idle + a flush-on-next-prompt fallback, using a session_v2/session_message reader that maps V2 rows onto the same SourceTurn payload (turnId = user session_message.id, which matches the prompt-admission messageID exactly; tool calls/results recovered from inline content[].state; compaction and revert handling ported),
  • ports the /memmy-resume flow unchanged.

Results on OpenCode V2 + Memory 2.1.4: recall injection works on every prompt, turns are captured with correct identity, queries are stored unwrapped, tool calls are paired, and episodes appear in the viewer. Capture works for parallel sessions too.

The port is published here: https://gist.github.com/kristhianmanue1/f2048c3b578ce14c2839327b87c492e8 (memmy-opencode-v2-plugin.js, MIT, derived from the official template). It is a stopgap: an official V2 template + a V2-aware db-reader would fix both the plugin and the desktop scan.

Suggested fix

  1. Ship a V2 plugin template (default-exported {id, setup} on @opencode/plugin) alongside the V1 one, choosing per detected OpenCode version, or a single dual-entry file (setup() for V2 + server() for V1, supported since OpenCode 1.18.29 per the migration guide).
  2. Extend AgentSourceCore's OpencodeSource implementation with a session_v2/session_message reader (fallback when the V1 tables are absent). Useful mapping notes: user text lives at data.text (no role field; the row type is the role), assistant parentID no longer exists so turns must be grouped by seq order (user row → following assistant rows), assistant completion is data.time.completed, and session_v2.revert keeps the same {messageID} JSON shape.

Happy to turn the attached port into a PR against either the template or AgentSourceCore if maintainers prefer.

— Kris Nóva

Activity

  1. kristhianmanue1 commented on Oct 27, 2026

    @kristhianmanue1
    Author

    Resolved by v1.2.4 (2026-10-22) — verified live on v1.2.401. Closing with a point-by-point mapping:

    Issue finding Resolution shipped in v1.2.4+
    Plugin ships the V1 plugin API (@opencode-ai/plugin, chat.message hooks) Template now renders export default { id, server, setup }: setup(ctx) is the V2 entrypoint with feature-detected context APIs; V1 hosts keep server(). The SDK import is gone entirely — the schema helper is bundled in the bridge (opencodeTool), so neither host generation needs the other package. Regenerated plugin identifies itself as revision opencode-v2-3.
    Both DB readers target the V1 schema (session/message tables) Reading moved to AgentSourceCore/src/opencode-database-source.ts, which handles the session_v2/session_message schema; the workspace-bridge runtime was updated accordingly, with a dedicated test at Memory/tests/service/session/source-turn-opencode-v2.test.ts.
    Follow-up finding: session.compacted never fires on OpenCode V2 (live tally on 2.0.29–2.0.30) The event layer now maps V2's session.compaction.ended to the internal session.compacted hook, and the regenerated plugin ships readOpencodeCompactionBoundary for boundary reads.

    Local verification: upgraded 1.2.2 → 1.2.401 (darwin-arm64, checksummed DMG). The installer regenerated the OpenCode plugin automatically, the memory service came up as 2.1.7 (schema 9 preserved), and recall/capture work end-to-end on OpenCode v2.0.30.

    TL;DR(中文):v1.2.4 起官方已原生支持 OpenCode V2 插件 API 与 session_v2 数据库架构(含 compaction 事件映射),已在 v1.2.401 上实测通过,本 issue 关闭。

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions