fix(v2): register pty tools, slash commands and exit notifications via the V2 plugin SDK - #61
Conversation
The V2 setup registered neither tools nor commands with OpenCode: - Tools: `ptyTools` was exported but never passed to `ctx.tool.transform`, so no `pty_*` tool ever reached OpenCode. Issue shekohex#55 explicitly asked to "register PTY tools through the V2 tool transform/registration surface". - Commands: `registerV2Commands` called `draft.update(name, cb)`, but OpenCode v2's CommandEditor exposes `add(definition)` only. The guard `typeof draft.update === 'function'` therefore always short-circuited and the slash commands never appeared in the "/" menu. Adapts the V1 tool definitions ({ description, args, execute }) to V2 Tool.Info ({ name, input, description, execute }), registers them through ctx.tool.transform, and switches commands to draft.add() with an execute handler. Tests now assert real registration (mocking add()) instead of mere module-level existence.
opencode v2's Tool.Info expects a JSON Schema for `input` (see the plugins docs), not a Zod schema. Convert the V1 Zod args via Zod v4's `toJSONSchema` instead of passing the Zod object through.
|
CI needs fixing, overall looks good. Would be nice if you can post a small video of it. |
The PR CI (test (format), Devenv CI) failed because biome wants the definition.execute(...) call on a single line.
…-ai/* 1.18
- add @opencode/plugin@^2.0.10 (V2 plugin SDK: Plugin.Context with the
session domain, used to type ctx.session in src/v2)
- bump @opencode-ai/plugin + @opencode-ai/sdk from ^1.3.13 to ^1.18
(kept for the V1 entry point); ToolResult is now string | { output },
adjusted the spawn integration test accordingly
The V2 entry point never wired a SessionNotifier: createV2Adapter() was called without options, so installHostAdapter skipped setNotifier and the fallback NotificationManager had no client - <pty_exited> was silently never sent, leaving agents that were told to wait for it stuck. - src/v2/notifier.ts: V2SessionNotifier delivers through the plugin context's session domain (ctx.session.prompt) with a deterministic message id (pty_<id>_exited) so admission.reconcile makes repeated deliveries idempotent. opencode v2 resolves the run with the session's current model, so the V1 setAgentModel clobber (shekohex#57) does not apply. - src/v2/index.ts: build the notifier from ctx in setup, guarded by typeof ctx.session?.prompt; hosts without the session domain get a visible warning instead of dead silence (shekohex#54) - src/plugin/pty/notification-manager.ts: export buildExitNotification so both notifiers share the message format - tests: live integration covers notification delivery + the no-session fallback
|
Quick heads-up on the dependency swap: the second commit migrates the V2 typing surface from the old
Happy to rebase/split if you'd prefer the dep bump as its own PR alongside #64 — just let me know. 🙂 |

Problem
The V2 entry point (
src/v2/index.ts) was nearly inert on real OpenCode v2 hosts:ptyTools(src/v2/tools.ts) was exported but never passed toctx.tool.transform, sopty_spawn/pty_write/pty_read/pty_list/pty_killnever reached OpenCode. Issue Plan OpenCode V2 plugin API support #55 explicitly listed "Register PTY tools through the V2 tool transform/registration surface" as part of the port.registerV2Commandscalleddraft.update(name, cb), but OpenCode v2'sCommandEditoronly exposesadd(definition). The guardtypeof draft.update === 'function'therefore always short-circuited, andpty-open-background-spy/pty-show-server-urlnever appeared in the/menu.createV2Adapter()was called without options, soinstallHostAdapternever installed a notifier and the fallbackNotificationManagerhad no client:<pty_exited>was silently never sent. Agents that were told to wait for it (perspawn.txt) simply stopped — indistinguishable from "the model decided not to respond" ([Bug]: exit notification delivery failures are silently swallowed — agent never wakes, no diagnostic #54).The existing tests did not catch any of this: they mocked a draft that happened to implement
update, only asserted module-level existence ofptyTools, and never exercised the notifier.Fix
src/v2/tools.ts: addregisterV2Tools(draft), adapting the V1 tool definitions ({ description, args, execute }) to V2Tool.Info({ name, input, description, execute }). TheargsZod shape is converted to a JSON Schema via Zod v4'stoJSONSchema, matching the plugin documentation.src/v2/commands.ts:registerV2Commandsnow usesdraft.add({ name, description, execute }).src/v2/index.ts:setupcallsctx.tool.transform(registerV2Tools); the adapter now receives aV2SessionNotifierbuilt from the plugin context'ssessiondomain (ctx.session.prompt).src/v2/notifier.ts(new): exit notifications are delivered as user prompts with a deterministic message id (pty_<id>_exited), soadmission.reconcilemakes repeated deliveries idempotent. opencode v2 resolves the run with the session's current model (session/runner/model.ts), so the user's model selection is preserved — the V1setAgentModelclobber tracked in [Bug]: pty exit notification omits model, clobbers user's session model selection (falls back to agent's configured model) #57 does not exist on the V2 path. Delivery failures are logged instead of swallowed ([Bug]: exit notification delivery failures are silently swallowed — agent never wakes, no diagnostic #54).src/plugin/pty/notification-manager.ts:buildExitNotificationis exported so V1 and V2 notifiers share the message format.src/v2/types.ts:ctx.sessionis typed from the official V2 SDK (@opencode/plugin); the hand-rolled context loses itsany-ish index signature for the domains we use.@opencode/plugin@^2.0.10(the V2 plugin SDK) and bumped@opencode-ai/plugin/@opencode-ai/sdk^1.3.13→^1.18(V1 entry only;ToolResultis nowstring | { output }, the affected test was adjusted).add()(what OpenCode actually provides) for tools and commands, plus live notification delivery and the no-session-domain fallback.Verification
bun run typecheck— cleanbun test test/opencode-v2-live.test.ts test/pty-spawn-echo.test.ts test/notification-manager.test.ts— passbun run lint,bun run format— cleanbun test— same 4 pre-existing failures as on the base commit (npm-pack asset tests + two timing-sensitive tests), no new failures@opencode-ai/plugin@0.0.0-beta-19271): all fivepty_*tools visible to the agent, both slash commands in the/menu,/pty-open-background-spystarts the web UI with live sessions. The notification path is validated in the live test against the real PTY lifecycle (shimmedctx.session.promptrecorder);ctx.sessionitself is source-confirmed in OpenCode v2.0.9's plugin host (packages/core/src/plugin/host.tsbuilds a context whosesessiondomain exposesprompt/get/switchModel/wait/…).Refs #55, #54, #57.