The WS-orphan reap interrupts the turn (deny-resolving the pending approval
queue) and teardown pops it via unregister_gateway_notify — both silently.
A reconnecting client's parked prompt card then answers resolved: 0 (or a
bare 4001 on approval.pending), so the prompt looked lost rather than
cancelled and the user had no signal the command would never run (#106678).
- session_lifecycle: _announce_cancelled_gateway_approvals broadcasts the
dropped entries (session ids, reason, request ids/count) before the
deny-resolve drains the queue, from both _interrupt_session_turn and
_teardown_session. Fail-open: a missing key, empty queue, or broadcast
failure never blocks the deny-resolve or teardown.
- _interrupt_session_turn gains orphan= so the reaper labels its cancels
ws_orphan_reap explicitly (never request_id prefix sniffing).
- contracts: ApprovalCancelledPayload event model + regenerated TS/OpenRPC.
- desktop: gateway-event lifecycle consumes approval.cancelled and clears
the parked prompt cards by request id (mismatch is a no-op, so a newer
prompt survives); the turn's BLOCKED tool result is the in-transcript
signal, same as the timeout path.
Supersedes #106693 (KoNit-K) and #126455 (Finn763); design credit to both.
Co-authored-by: KoNit-K <konit.block@protonmail.com>
Fixes#106678