fix(desktop): never block the JS thread on the error-report dialog
Unhandled rejections and uncaught errors in a desktop app called the
blocking op_desktop_alert on the JS thread. DesktopApi::alert blocks
until the dialog is dismissed, so the entire runtime - timers, servers,
signal handlers - froze until someone clicked the dialog, and forever if
nobody could (hidden window, headless-ish child process). SIGTERM was
ignored too, since the handler is JS on the blocked thread (#36393).
Error reporting now:
- always logs the message and stack to stderr first (previously
unhandled rejections printed nowhere in desktop apps);
- shows the dialog via a new fire-and-forget op_desktop_alert_detached,
which runs DesktopApi::alert on a detached thread and returns
immediately, keeping at most one error dialog on screen instead of
stacking one per rejection.
The blocking op_desktop_alert is unchanged for renderer-initiated
alert(), which wants blocking semantics.
Fixes #36393
Claude-Session: https://claude.ai/code/session_016w9C7umSW1KUYa7eXwSXRo