Native OpenCode v2 adapter as a sibling package

Author: DEAN-CherryCreated Sep 7, 2026Updated Sep 17, 2026
Labelslazycodexopencode

Summary

I have a working native v2 adapter for OMO running against opencode2, built as a package sibling to omo-codex / omo-senpi rather than as a compatibility bridge inside omo-opencode. Before investing in splitting it into reviewable PRs, I would like to know whether this shape is one you would accept.

Related: #6169 (v2 migration timeline, open since July, acceptance question unanswered), #7847 (v2 loader rejects the { id, server } shape).

Why a new issue rather than a comment on #6169

#6169 asks for a timeline. This asks a narrower and more actionable question: is an independent sibling adapter package the right shape for v2 support, and would you review one? Two prior attempts (#7104, #7570) took the opposite approach and were both withdrawn by their author within two hours, without review, so there is no precedent to read either way.

Approach: sibling adapter, not a bridge

Both #7104 and #7570 placed v2 support inside packages/omo-opencode/src/plugin/v2/ as a compat facade replaying the v1 init pipeline. #7570's own description lists the resulting cost: todo/boulder continuation degrade to no-ops, compaction hooks have no v2 surface, and the host contract is validated by QA rather than by the compiler.

This adapter instead registers natively against the v2 API and consumes only the Core layer:

  • 13 workspace core dependencies (model-core, prompts-core, rules-engine, team-core, omo-config-core, boulder-state, hashline-core, delegate-core, comment-checker-core, agents-core, shared-skills, utils, plus the plugin/schema pins)
  • zero dependency on omo-opencode

That is the dependency direction ROADMAP.md specifies for adapters, so it adds a harness without adding a reverse edge or a second consumer of the v1 pipeline.

Pinned to @opencode-ai/[email protected].

Registered surface today

Surface Detail
Agents 11 (4 primaries, sisyphus default) + delegation categories, via agent.transform
Tools 10 base + 19 config-gated, including 12 team_*
MCP 4 built-ins (context7, grep_app, lsp, codegraph) via ctx.mcp.transform; websearch deliberately omitted because v2 ships ctx.websearch natively
Skills 17 shared SKILL.md via skill.transform
Hooks tool guards, comment-checker, rules injection, hashline read+edit pairing, and one ordered session.hook("context") composer
Features Team Mode (mailbox), monitor, BTW side conversations, reactive model fallback, goal/todo/boulder idle continuation

Gates: bun test354 pass / 0 fail across 62 files; tsgo --noEmit clean. Per-increment QA against the real binary is recorded as 38 evidence bundles, asserted from OMO_SPIKE_TRACE NDJSON rather than CLI prose.

Correction to #7847

That issue is currently the most visible statement of what v2 can do, and one of its conclusions is not correct. It states that prompt mutation (chat.message, messages.transform, system.transform) and tool interception (tool.execute.*) "have no v2 equivalents to bind to".

They do:

  • session.hook("context") exposes event.system, event.messages and event.tools together. It is the prompt-rewrite and tool-stripping surface. The adapter uses exactly one of these as an ordered composer: sisyphus prompt rebake, skill catalog, command catalog, rules, then keyword mode last.
  • tool.hook("execute.before" / "execute.after") covers interception.
  • agent.transform, mcp.transform, skill.transform and catalog.transform are the other high-leverage surfaces the issue's "portable subset" omits.

So the harness is portable to v2 in substance, not only loadable.

Real v2 constraints worth recording

These are genuine and shaped the design:

  • The model catalog is empty at setup and populates asynchronously via catalog.updated. A setup-time snapshot silently drops every agent onto its first fallback.
  • agent.transform callbacks stay lazy until first registry materialization; agent.reload() forces them.
  • SessionDomain has no list and no messages, so session-history tools cannot be built on ctx.session and must read the SQLite store read-only.
  • session.hook("model.request") has a readonly model field, so model fallback has to be reactive off session.execution.failed + switchModel, never proactive.
  • ctx.shell is { hook("create.before") } and nothing more; the full ShellApi hangs off the HTTP client surface, which the plugin Context never hands out.
  • aisdk hooks only fire for models the native map cannot resolve, so they are not a viable interception point for the major providers.

Questions

  1. Is an independent sibling adapter package the shape you would accept for v2, or do you want that surface handled differently (or reserved)?
  2. If yes, would you review it split per milestone — agents+prompts, then tools/orchestration, then hooks/context, then MCP, then features?
  3. Given LazyCodex and the Senpi edition, is the OpenCode plugin line still one you intend to extend to v2? A plain "not planned" is a genuinely useful answer and would save everyone the PR work.

Happy to sign the CLA if there is interest.

Source: code-yeongyu/oh-my-openagent