Repository navigation
fix: ring the bell when an opencode session asks a question - #14
Merged
Merged
Conversation
An opencode session blocked on an interactive question looked exactly like one that was working: spinner, and the tool line reading `question`. No bell, no toast, no desktop notification — the session waited until somebody happened to look at it. Only permissions were reported. A question rides an event of its own, `question.asked`, and the module listened for `permission.asked` by name, so the last thing the card heard was the `busy` from the tool call that asked. A run against a stub confirms nothing follows it: opencode reports no status at all while the question is pending, so the spinner is not stale for a moment, it is stale for as long as the user takes. Matched by suffix rather than by name. opencode asks in four spellings today — `permission.asked`, `question.asked` and a `.v2.` variant of each — and its catalogue is not exhaustive: a real run emits types (`server.heartbeat`) that its published schema does not list. Enumerating names is precisely how this bug happened, and the failure modes are not symmetric. A surplus bell is cleared by the next status report, which a reply produces within ~100ms; a name nobody enumerated is a session waiting behind a card that says it is busy. Neither `question.replied` nor `question.rejected` is registered: both are followed by a status of their own — `busy` on an answer, `idle` on a dismissal — so the card re-arms itself, and a second report would only say what the first already did.
This was referenced Aug 11, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
An opencode session blocked on an interactive question looked exactly like one that was working: spinner, and the tool line reading
question. No bell, no toast, no desktop notification.Only permissions were reported. A question rides an event of its own,
question.asked, and the module listened forpermission.askedby name — so the last thing the card heard was thebusyfrom the tool call that asked.What a real run shows
opencode serve1.18.16, driven through its own HTTP API, SSE on/event:So the spinner is not stale for a moment; it is stale for as long as the user takes. And the recovery needs nothing from us: answering and dismissing each produce a status of their own, which is why
question.replied/question.rejectedare not registered here.Why by suffix, not by name
opencode asks in four spellings today —
permission.asked,question.asked, and a.v2.variant of each — and its catalogue is not exhaustive: the same run emitsserver.heartbeat, which appears in none of the 94 event schemas its/docpublishes. Enumerating names is precisely how this bug happened.The failure modes are not symmetric. A surplus bell is cleared by the next status report, which a reply produces within ~100ms. A name nobody enumerated is a session waiting behind a card that says it is busy.
Tests
tests/opencode.test.mjscovers all four.askedtypes, an unknown one (elicitation.asked— the point of the rule), the bound (question.asked.dismissedmust report nothing), and a question raised inside a sub-session. Checked against the pre-fix module: the four new cases fail there and pass here, andpermission.askedpasses in both.Contract
docs/session-state.mdanddocs/providers.mdhere now describe the rule instead of the name. The canonical contract in the lich repository (docs/hooks/session-state.md:42) still listspermission.askedin its opencode column — a follow-up there, no lich code change:waitingis an existing state and the payload is unchanged.