Skip to content

Server plugins can't reply to forms (ctx.session.form is missing) #51108

Open

Description

@pedropombeiro

In OpenCode 2.0.16, the server plugin context exposes ctx.permission.reply but no way to reply to a form. SessionDomain in @opencode/plugin/dist/promise/session.d.ts picks create, get, switchAgent, switchModel, prompt, generate, command, synthetic, interrupt, update, move, wait and context from SessionApi, and form isn't in that list. The runtime object matches: Object.keys(ctx.session) has no form.

This blocks server plugins that answer questions from an external system. opencode-homeassistant polls a Home Assistant entity and replies to permission requests and question tool forms. Permissions work through ctx.permission.reply. For forms, the plugin has to emit an RPC event that a companion CLI plugin turns into context.data.session.form.reply, so remote answers only work while a terminal interface is connected.

Could SessionDomain include form (at least list, get, reply and cancel)?

Two small docs mismatches I found while porting:

  • The plugins guide uses ctx.session.rename, but the context only has ctx.session.update({ sessionID, title }).
  • The same guide calls ctx.permission.reply({ sessionID, requestID, reply: 'once' }), but PermissionReplyInput expects decision.

Activity

  1. holny commented on Sep 24, 2026

    @holny
    Contributor

    I'll take this. Confirmed against current v2: SessionDomain in packages/plugin/src/promise/session.ts (and its Effect counterpart) picks a fixed key list from SessionApi and form isn't in it, while the runtime ctx.session mirrors that projection — so a server plugin has no form surface at all.

    Plan: add form to SessionDomain for both the Promise and Effect plugin surfaces, bridging list/get/reply/cancel through the existing host like permission.reply does. Also fix the two docs mismatches you flagged (ctx.session.rename → ctx.session.update, reply → decision).

    Fixes #51108

  2. added 2 commits that reference this issue on Oct 4, 2026
    69e00d1
    27db2a5
  3. rullerzhou-afk commented on Oct 9, 2026

    @rullerzhou-afk

    Adding a concrete downstream use case for this.

    Clawd on Desk is a desktop companion for coding agents. For OpenCode, it already shows permission prompts as desktop cards through ctx.permission.hook("evaluate") on 2.x. On 1.x, we're adding direct answers to question tool prompts through the native question API (rullerzhou-afk/clawd-on-desk#1163).

    On 2.x, the question tool goes through Form, but the plugin context has no form surface, so there is no supported way to provide the same experience. The only workaround would be calling the service HTTP API from outside the plugin, which means discovering its URL and credentials. We'd rather not ship that.

    The ctx.session.form.list/get/reply/cancel API proposed in #51142 is exactly what we'd build on. We don't need plugins to create forms; listing, inspecting, replying to, and cancelling pending forms is sufficient for this use case.

    One related question: are form.created, form.replied, and form.cancelled delivered to server plugins through ctx.event.subscribe()? If so, a plugin can react when a form appears instead of polling ctx.session.form.list.

  4. added a commit that references this issue on Oct 21, 2026
    ab605a3
  5. Hamza-Abili commented on Oct 26, 2026

    @Hamza-Abili

    Downstream use case +1. opencode-telegram-notify (npm: @hamzaabili/opencode-telegram-notify) sends permission requests and question forms to a Telegram chat. Permission taps work today through ctx.permission.reply({decision}) (verified on 2.1.4). Question taps are implemented behind a surface probe (ctx.session.form.reply / ctx.question.reply) but on 2.1.x the plugin context exposes neither, so forms fall back to notify-only with a terminal pointer — no dead buttons. form.created/replied/cancelled do arrive via ctx.event.subscribe(), so the plugin can react to forms; it only lacks the reply/cancel surface to answer them. Once ctx.session.form (or an equivalent question surface) lands, the plugin enables in-chat answers automatically with no further changes on our side. Happy to test against a dev build if useful.

  6. opencodejr commented on Oct 28, 2026

    @opencodejr
    Contributor

    Thanks for the detailed write-up, @pedropombeiro!

    You're asking for server plugins to be able to answer forms. That means exposing ctx.session.form (list, get, reply, cancel) the same way ctx.permission.reply already works for permissions. Then integrations like opencode-homeassistant could answer question forms without a connected terminal interface. I checked the current code: SessionDomain still doesn't include form, though the HTTP API and client already support these operations. The maintainers will consider it.

    Related:

    On the two docs mismatches: the current plugins guide source already uses ctx.session.update({ sessionID, title }) and ctx.permission.reply({ sessionID, requestID, decision: "once" }), so the published page should show those once it's updated.

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