Skip to content

[bug]: Inline slash command output leaves lifecycle-aware clients stuck on "working" #272

Description

@niheaven

Pre-flight checks

  • I searched existing issues and did not find a duplicate.
  • I verified this on the current production released OpenCode version, or I explain why not below.

OpenCode version tested

1.18.30

opencode-quota version tested

4.9.0

Bug summary

The deterministic slash commands inject their output with session.prompt({ noReply: true }), which fires chat.message, but nothing publishes an end-of-turn event afterwards: the TUI inline handler (the default) returns right after the injection, and the server command path throws the COMMAND_HANDLED sentinel and fails with HTTP 500. Lifecycle-aware integrations (e.g. Herdr's opencode plugin) stay stuck on "working" until the next real prompt.

Steps to reproduce

  1. Install opencode-quota 4.9.0 with the server plugin loaded ("plugin": ["@slkiser/opencode-quota@latest"]).

  2. Run an OpenCode server with a plugin that logs bus events, e.g.:

    // .opencode/plugin/event-log.js
    export const EventLogPlugin = async () => ({
      "chat.message": async ({ sessionID }) => console.log("chat.message", sessionID),
      event: async ({ event }) => console.log(event.type, event.properties?.sessionID),
    });
  3. Reproduce the plugin's injection shape:

    $sid = (Invoke-RestMethod -Method Post http://127.0.0.1:4096/session -ContentType application/json -Body '{}').id
    Invoke-RestMethod -Method Post "http://127.0.0.1:4096/session/$sid/message" `
      -ContentType application/json `
      -Body '{"noReply":true,"parts":[{"type":"text","text":"quota output","ignored":true}]}'
  4. Observe the event stream, or just run /quota in the TUI (default inline display) with a lifecycle watcher attached.

Expected behavior

Like any normal command, the session emits session.idle (and command.executed) after chat.message, so lifecycle watchers return to idle.

Actual behavior

The noReply injection produces no end-of-turn event:

chat.message         ses_…   <- watchers now report "working"
message.updated      ses_…
message.part.updated ses_…
session.updated      ses_…
(nothing else — stuck "working" until the next real prompt)

The server /command surface additionally returns 500 UnknownError (err_…) for /quota.

Relevant logs/output

Observed on opencode 1.18.30 + quota 4.9.0 with an event-logger plugin on the same bus:

  • Raw noReply injection: last event is session.updated, no idle after chat.message.
  • session.abort on the stuck session immediately emits session.status + session.idle, so the settle primitive exists and is cheap to call from the plugin.

If not tested on current production OpenCode, explain why

N/A — tested on current production release.

Activity

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

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions