From 325bf084eee01d3191a9fab7c8c6adb0365e4908 Mon Sep 17 00:00:00 2001 From: omartelo Date: Tue, 11 Aug 2026 15:40:39 -0300 Subject: [PATCH] docs: opencode reports waiting for any prompt, not one event MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The opencode column of the state table named `permission.asked`, which was the whole client until a session asking an interactive question was found waiting behind a card that showed a spinner: a question rides an event of its own, and enumerating names is how it went unreported. The plugin (v0.9.1) now matches any type ending in `.asked` — four spellings today, and the catalogue is not exhaustive enough to enumerate against: a real run emits types the 94 event schemas opencode publishes do not list. The contract is canonical here, so the rule is written here. Nothing about the contract itself moves: `waiting` is an existing state, the payload is unchanged, and the fixtures are untouched. Per the versioning rule in README.md this is a change *within* a contract — a plugin-only release, which is why lich needs no code change. Also records a ceiling the audit turned up. Two `waiting` reports for one prompt are two toasts and two desktop notifications: the toast reads the raw event on purpose, and decideStatusNotice weighs `previous` for `done` but never for `waiting`. Harmless while a harness raises one event per prompt, which they all do — but it names where a duplicate has to be dropped, which is the client, not here. --- docs/hooks/session-state.md | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/docs/hooks/session-state.md b/docs/hooks/session-state.md index 7b8ea0ed..2aad9c26 100644 --- a/docs/hooks/session-state.md +++ b/docs/hooks/session-state.md @@ -39,7 +39,7 @@ Both sides test against the payloads in | `UserPromptSubmit` | `UserPromptSubmit` | `session.status` (`busy`) | — | `busy` | | `PreToolUse` | `PreToolUse` | `tool.execute.before` | — | `busy` + `tool` | | `PostToolUse` | `PostToolUse` | `tool.execute.after` | — | `busy` | -| `Notification` | `PermissionRequest` | `permission.asked` | — | `waiting` | +| `Notification` | `PermissionRequest` | any `*.asked` | — | `waiting` | | `Stop` | `Stop` | `session.status` (`idle`) | — | `done` | | `SessionEnd` | — | — | — | `idle` | @@ -49,6 +49,15 @@ same payload. Its `idle` means the turn ended, which is lich's `done` — not lich's `idle`, which says the CLI itself has left. Nothing in opencode's event list says that, so like Codex it never reports `idle`. +**opencode's `waiting` is a rule, not an event name.** It asks the user in more +than one way — a permission decision and an interactive question, each with a +`.v2.` spelling published alongside — so the client reports `waiting` for any +event type ending in `.asked` rather than for a list of names. Its catalogue is +not exhaustive to enumerate against: a real run emits types (`server.heartbeat`) +that the 94 event schemas its own `/doc` publishes do not list. Nothing is +registered for the answer: replying raises `session.status busy` and dismissing +raises `session.status idle`, each within ~100ms, so the card re-arms itself. + **Crush reports no state at all.** Its only hook event is `PreToolUse`, and a `busy` with nothing that can end it would leave a spinner on the card until the next turn — a state that is wrong for longer than it is right. So the plugin @@ -172,6 +181,14 @@ a broken script could do was lose a status report. not clear when the session leaves `waiting` (user handled it in the terminal). Fix path: track the toast id per session and dismiss it on the next busy/done. +- **Two `waiting` reports for one prompt are two toasts and two desktop + notifications.** The toast reads the raw event by design (a repeat state is no + change to the store, which would swallow the second one), and + `decideStatusNotice` weighs `previous` for `done` but never for `waiting` — a + session already waiting notifies again. Harmless while a harness raises one + event per prompt, which every one of them does today; it is the cost a client + pays for reporting the same block twice, and the reason to dedupe on the + client rather than here. - `PostToolUse → busy` recovers from `waiting` only when Claude runs a tool. Deny a permission and let Claude end the turn without another tool and the card stays `waiting` until `Stop → done`. Rare, and it self-corrects on the next