Skip to content

Support OpenCode 2 and keep OpenCode 1 - #4

Merged
benkelly merged 2 commits into
mainfrom
opencode-v2
Oct 22, 2026
Merged

benkelly merged 2 commits into
mainfrom
opencode-v2

Conversation

@benkelly

@benkelly benkelly commented Oct 22, 2026 •

Copy link
Copy Markdown
Owner

The plugin did not load on OpenCode 2. This change makes one package work on OpenCode 2 and on OpenCode 1. Closes #2.

Problem

OpenCode 2 does not run v1 plugin code. It requires a default export with an id and a setup() function, so it rejected the plugin function. OpenCode 2 also changed the events that the plugin reads:

  • Event data moved from event.properties to event.data.
  • A turn ends with session.execution.succeeded, session.execution.failed, or session.execution.interrupted.
  • OpenCode 2 sends no session.idle and no session.error.

Without the new events, the device stays on the busy animation after every turn.

Fix

One default export serves both APIs. OpenCode 1 calls server(). OpenCode 2 calls setup().

flowchart LR
  A[default export] -->|OpenCode 1| B["server() returns v1 hooks"]
  A -->|OpenCode 2| C["setup() registers hooks + event stream"]
  B --> D[shared handlers]
  C --> D
  D --> E[report: app socket or LEDS.LED]
Loading
v1 v2
chat.message ctx.session.hook("prompt")
tool.execute.before and tool.execute.after ctx.tool.hook("execute.before" / "execute.after")
event ctx.event.subscribe(), aborted in cleanup
session.idle session.execution.succeeded
session.error (MessageAbortedError means cancel) session.execution.failed / session.execution.interrupted

The named SidePulse export is gone. A v1 loader can load a named export as a second instance, and two instances fight over the device. The package stays free of dependencies, and it does not import @opencode/plugin.

The self-check replays three traces that I captured from OpenCode 2.1.2 with a logging probe plugin: a normal finish, a cancel, and a failure. It also makes sure that the default export has id, server(), and setup().

Compatibility

The migration guide says that v1 object entrypoints need OpenCode 1.18.29 or newer. I tested older: on 1.17.20, server() runs once and the plugin works. The README states 1.17.20 or newer, and it tells users of older releases to pin opencode-sidepulse@0.1.7. The version goes to 0.2.0, because the named export is gone. The merge publishes it.

How to test

The self-check needs no device:

node plugin.mjs

The first line must be ok: 5 programs, 7 transitions. If you remove the session.execution.succeeded line from signalForEvent(), the command must fail with v2 normal finish: after session.execution.succeeded state is busy, want done.

I also ran real turns on two Macs: a Mac mini (macOS 26.4.1, Apple Silicon, Node v26.12.1) with a SidePulse Dot, and a MacBook (macOS 15.8.1, Apple Silicon, Node v26.5.0) with a SidePulse in the SD card reader. Each OpenCode version ran with its own XDG folders, and it loaded only this plugin from a local path.

OpenCode Mode Scenario Result
2.1.2 direct echo hi through the shell tool ends green #00ff22
2.1.2 direct Ctrl+C during sleep 20 ends green (cancel, as on v1)
2.1.2 direct model opencode/no-such-model-xyz ends red #ff2000
2.1.2 direct permission prompt, answered once after 4 s amber #ffb000 while it waits, then ends green
2.1.2 app stand-in socket server UserPromptSubmit, PreToolUse shell, PermissionRequest, UserPromptSubmit, PostToolUse shell, Stop. No write to LEDS.LED
1.19.3 direct echo hi through the bash tool ends green #00ff22
1.17.20 direct load only server() runs once
2.0.24 (desktop app CLI) direct normal, cancel, failure, permission on a SidePulse in the SD card reader green, green, red, amber then green
1.17.20 direct load and start of a turn on the same SidePulse server() runs once, the plugin writes the device

opencode run rejects a permission prompt at once, so a test plugin drove the permission turn from inside the service with ctx.session.prompt and answered it with ctx.permission.reply. The app-mode test used a Unix socket server at the probed path that logged each message. It was not the real SidePulse app.

Known limits, not changed here

  • The plugin keeps one "turn active" flag for all sessions. If a Stop from another session arrives during a turn, the plugin drops that turn's tool events. In my app-mode run, a resumed earlier session caused exactly this. v1 has the same flag.
  • On OpenCode 1, a failed turn ends green. On 1.19.3 the released 0.1.7 does the same with an unknown model, so this PR does not change it.

OpenCode 2 does not run v1 plugin code. The plugin now has one default
export with id and setup() for v2, and server() for v1. The named
SidePulse export is gone, because a v1 loader can load it as a second
instance.

OpenCode 2 ends a turn with session.execution.succeeded, .failed or
.interrupted, and sends no session.idle or session.error. Event data
moved from properties to data. The self-check replays three traces
captured from OpenCode 2.1.2.

Closes #2
Copilot AI balanced review requested due to automatic review settings October 22, 2026 17:10

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@benkelly

Copy link
Copy Markdown
Owner Author

Hi @MathisHammel,

Apologies for the delay of me getting to this. thanks for flagging v2 support :)

The plugin loads on OpenCode 1.17.20: server() runs once. The
migration guide names 1.18.29 as the minimum, but that is more
conservative than the loader.
Copilot AI balanced review requested due to automatic review settings October 22, 2026 17:24

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@benkelly benkelly changed the title Support OpenCode 2 and keep OpenCode 1.18.29+ Support OpenCode 2 and keep OpenCode 1 Oct 22, 2026
@benkelly
benkelly merged commit 9a93aa4 into main Oct 22, 2026
2 checks passed
@benkelly
benkelly deleted the opencode-v2 branch October 22, 2026 17:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support for OpenCode v2?

2 participants