Skip to content

fix(opencode): migrate adapter to the OpenCode v2 plugin API - #596

Merged
garysheng merged 21 commits into
PeonPing:mainfrom
MwC-Trexx:fix/opencode-v2-plugin-api
Oct 4, 2026
Merged

garysheng merged 21 commits into
PeonPing:mainfrom
MwC-Trexx:fix/opencode-v2-plugin-api

Conversation

@MwC-Trexx

Copy link
Copy Markdown
Contributor

Summary

The OpenCode adapter does not load at all on opencode >= 2.0. This migrates it to the v2 plugin API and to the v2 event taxonomy.

Two independent changes landed in v2 and both are required for the adapter to work.

1. Plugin module format

v2 removed the v1 server plugin API entirely. Local plugins under ~/.config/opencode/plugin{,s}/ are decoded by packages/core/src/plugin/module.ts, which requires the default export to be a definition object:

Schema.Struct({
  default: Schema.Union([
    Schema.Struct({ id: Schema.String, effect: ... }),
    Schema.Struct({ id: Schema.String, setup: ... }),
  ]),
})

This adapter's default export is a bare async function (the v1 shape), so it fails:

PluginModule.LoadError: Plugin must export a default definition with an id and
an effect or setup function.
cause: SchemaError(Expected object at ["default"])

Expected object rather than Missing key is the tell — the key is present, it just is not an object.

2. Event taxonomy

Every event the adapter switched on is gone. session.idle survives only as a // deprecated definition, and session.status is defined but never published by the server. v2 payloads also carry the event body under data rather than properties.

peon-ping hook event v1 event (removed) v2 event used instead
SessionStart session.created first session.execution.started
UserPromptSubmit session.status (busy) session.execution.started
Stop session.idle session.execution.succeeded
PostToolUseFailure session.error session.execution.failed
PermissionRequest permission.asked permission.asked (unchanged)
Notification question.asked form.created

Behaviour changes to be aware of

  • PostToolUseFailure now fires for any failed execution, not only tool errors — there is no longer a per-tool error event. The task.error peon.sh category is unchanged, so this is a superset of the old trigger.
  • session.execution.started doubles as both SessionStart and UserPromptSubmit. SessionStart is emitted first so the existing 3s debounce suppresses the UserPromptSubmit immediately after it, preserving pre-v2 behaviour.
  • The elicitation surface was renamed question.* -> form.*, and questions are no longer nested under a questions key. Only the request id is read, and it is still never forwarded to peon.sh (covered by the existing sentinel test).
  • The adapter is now untyped. @opencode-ai/plugin is not resolvable from the plugins directory, so depending on it would break loading.

Verification

  • bun run test in adapters/opencode: 59 passed (49 internals + 10 adapter).
  • Live against opencode v2.0.16: the plugin loads with no failed to load plugin entry, and session.execution.started produces a real SessionStart dispatch to peon.sh ([route] category=session.start suppressed=False).
  • All six mapped hook events were driven through a fake v2 host context and observed reaching peon.sh with the correct hook_event_name, cwd and notification_type.

Also added

docs/opencode-v2-events.md documenting the schema, the payload shape change, and the full mapping table.

Note for the installer

adapters/opencode.sh downloads this file from main, so it will pick up this change once merged. No installer change is needed.

OpenCode v2 removed the v1 server plugin API, so this adapter no longer
loads at all on opencode >= 2.0:

  PluginModule.LoadError: Plugin must export a default definition with an
  id and an effect or setup function.
  cause: SchemaError(Expected object at ["default"])

v2 requires the default export to be a definition object
(`{ id, effect }` or `{ id, setup }`); this adapter exported a bare async
function. 'Expected object' rather than 'Missing key' because the key is
present but is not an object.

The event taxonomy changed at the same time, so every event the adapter
switched on is gone. v2 payloads also carry the body under `data` rather
than `properties`. Mapping:

  SessionStart        session.created            -> session.execution.started (first)
  UserPromptSubmit    session.status (busy)      -> session.execution.started
  Stop                session.idle               -> session.execution.succeeded
  PostToolUseFailure  session.error              -> session.execution.failed
  PermissionRequest   permission.asked           -> permission.asked (unchanged)
  Notification        question.asked             -> form.created

`session.idle` survives only as a deprecated definition and
`session.status` is defined but never published.

Behaviour notes: a failed execution now reports PostToolUseFailure for any
failure rather than tool errors only, and PostToolUseFailure no longer has
a per-tool trigger. Elicitation ids are still never forwarded to peon.sh.

Adds docs/opencode-v2-events.md and updates the adapter tests to the v2
context contract.
@vercel

vercel Bot commented Sep 25, 2026

Copy link
Copy Markdown

Someone is attempting to deploy a commit to the Gary Sheng's projects Team on Vercel.

A member of the Team first needs to authorize it.

rafaelsnv and others added 17 commits October 2, 2026 19:53
- Add adapters/opencode/peon.sh: Windows wrapper that pipes JSON from
  stdin to peon.ps1 via -File flag (supports stdin redirection)
- Fix scripts/win-play.ps1: check for ffplay first on mp3/wav/wma since
  WPF MediaPlayer hangs in headless/server environments (5s timeout)
  before falling back
OpenCode (v1 and v2) uses XDG-style paths (~/.config/opencode/) on all
platforms, not Windows-specific LOCALAPPDATA. The installer was using
%LOCALAPPDATA%\opencode\plugins\ which is incorrect.

Update path logic to use ~/.config/opencode/ as the primary path, matching
OpenCode's actual plugin discovery paths documented in v1 and v2 docs.
…pendency

The OpenCode TypeScript plugin previously hardcoded spawn("bash", ...),
requiring Git Bash or WSL to be installed and in PATH. On Windows
PowerShell, cmd.exe, or any non-bash terminal, the plugin would silently
fail because bash could not be found.

Changes:
- Plugin now detects platform: Windows spawns powershell.exe directly
  against peon.ps1, Unix keeps the existing bash + peon.sh path
- Plugin searches hooks dirs for both peon.sh and peon.ps1
- install.ps1 copies adapters/opencode/peon.sh to the hooks dir so the
  Unix wrapper is present even for users who never run bash
GitHub raw.githubusercontent.com URLs cache for ~5 minutes, which
caused the installer to fetch stale peon-ping.ts (missing Windows
PowerShell spawn fix). Switching to cdn.jsdelivr.net/gh bypasses the
cache and serves the latest committed version immediately.

Refs: rafaelsnv/peon-ping@opencode-windows-adapter
The upstream PeonPing/peon-ping doesn't have the PowerShell-spawn fix
yet (waiting for PR PeonPing#599 merge). For testing, fetch from the fork so
users get the bash-free plugin immediately.

Revert this before merging PR PeonPing#599 upstream.
…lity

Integrate PRs PeonPing#596 and PeonPing#599 through d0ab8c1 in an isolated triage candidate. Use official v2 sessionID and nested data.form payloads, gate duplicate starts, clear interruption state, and filter scoped server events by plugin location. Route Windows JSON directly to PowerShell and retain Unix Bash routing, explicit hook directories and OpenPeon discovery. Contain asynchronous launch and stdin errors.

Keep Kilo on its v1 server and properties contract in a dedicated adapter. Both Kilo installers download that adapter directly instead of applying fragile sed/regex patches. Omit the obsolete Windows Bash wrapper and its installer download.

Validation: 71 Vitest tests and 21 OpenCode Bats checks pass on macOS after red/green regressions. The OpenCode source typechecks with explicit Node 22 types. Native Windows executable and Pester installer checks are prepared for CI but were not executable on this macOS machine; this candidate is not Windows merge-ready until those jobs pass. Live OpenCode v2 host smoke also remains pending.
Include PR PeonPing#599 head c2571cd in the candidate ancestry. Its only delta since d0ab8c1 temporarily points the Windows installer at the contributor fork for testing. Keep the official PeonPing download URL for the maintained installer; the native installer regression checks that download boundary.

The previous local 71-test Vitest and 21-check Bats results cover the adapter behavior. Native Windows bridge and Pester checks still require CI on this candidate, and live OpenCode v2 host smoke remains pending.
…/rafaelsnv/peon-ping into opencode-windows-adapter"

This reverts commit d10c42c, reversing
changes made to 1a65654.
The contributor restored the official download URL, removed the obsolete Git Bash wrapper and installer block, and simplified discovery to the platform-native script. The combined candidate already contains these behaviors. Retain the verified v2 event adapter, extended discovery and asynchronous launch error handling while recording the latest contributor head in integration ancestry.
Synchronize Japanese, Chinese and public model-facing integration docs with the corrected candidate. Documentation-only; runtime verification is recorded on the preceding implementation commit.
…r tests

Official OpenCode 2.0.22 exposes unscoped durable execution events and can load the same physical project through multiple path aliases. Resolve session locations before forwarding, canonicalize directories, and assign one active bridge per physical directory. Release ownership on cleanup and reject lookups that complete after cleanup. Deleted sessions clear silent local state without retrieving a removed host record.

Pester 6.2.0 reproduced all three Windows installer failures: its TestDrive persisted between It blocks, and the mock counter's script scope belonged to the invoked installer. Use a fresh sandbox per test and Pester call-history assertions with exact official URL filters. Preserve wrong-location and content assertions.

Validation: red/green regressions for aliases, global routing, duplicate owners, cleanup races and deleted-session lookup; 77 Vitest and 21 Bats tests pass; Pester installer fixtures change from 0/3 to 3/3 under isolated official PowerShell 7.6.6. Exact adapter passes 10 checks in official OpenCode 2.0.22, including real nested forms, permissions, success/failure, child suppression, another project and three reloads with 12 closed streams. Dedicated Kilo adapter loads in official Kilo 7.8.3 and handles real primary/child session.created payloads. Strict production source typecheck passes. Fresh native Windows adapter and full Pester CI remain a merge gate.
Pure documentation correction. Verified the dedicated source URL and XDG directory in kilo.sh and the installed main peon.sh route in the unchanged Kilo plugin. Synchronize all three README sections and the public guide; inactive legacy adapter config is not the playback configuration.
Two independently-created sessions in one Kilo plugin instance emitted the same setup-time peon session ID, defeating independent pack selection. Route every existing hook through the native session identity while retaining the kilo namespace and the timestamp fallback for legacy id-less events. Preserve event mappings, child suppression, rate guards and cleanup.

Validation: nine regressions fail before the fix; all 87 adapter tests pass afterward. Official unchanged Kilo 7.8.3 reproduces the failure and verifies distinct primary identities, matching completion identities, silent children and stable identity after native instance dispose/reload. Only the dedicated Kilo adapter and its compatibility tests change.
Synchronize the English, Japanese, Chinese and public setup guides with the native session identity correction. Documentation-only follow-up; 87 adapter tests and actual Kilo 7.8.3 session creation, completion, child suppression and reload checks passed on unchanged runtime bytes.
@garysheng
garysheng merged commit 70a85b0 into PeonPing:main Oct 4, 2026
6 checks passed

This branch was successfully deployed

1 active deployment
Preview — f26b58ef Deployed Oct 4, 2026 by vercel[bot]
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.

3 participants