hermes-agent/plugins/platforms
Spark (DGX) 3c7cff843d fix(photon): dispatch fatal-error notification from a detached task
Follow-up to #69112. That PR hardened the shared gateway dispatch path in
gateway/run.py against the *caller* being cancelled. A second, self-referential
cancellation specific to PhotonAdapter sits one layer underneath it and survived
that fix.

PhotonAdapter is the only platform adapter that awaits _notify_fatal_error()
inline, on the same task that detected the fault. Both _monitor_sidecar_health
and _supervise_sidecar run as self._sidecar_health_task /
self._sidecar_supervisor_task, and the notification routes into
GatewayRunner._handle_adapter_fatal_error_impl, which tears the adapter down via
_safe_adapter_disconnect -> disconnect(). disconnect() then cancels
self._sidecar_health_task and awaits it -- which, when the health task is what
raised the notification, means disconnect() cancels its own caller several
plain-await frames up.

disconnect()'s `task is not asyncio.current_task()` guard does not catch this.
The current task where that guard evaluates is the wrapper
_await_adapter_cleanup_with_timeout creates around disconnect() via
asyncio.ensure_future, not the health task further up the chain, so the guard
passes and the cancel lands.

CancelledError stopped subclassing Exception in Python 3.8, so the
`except Exception` that wrapped the inline notify call never saw it. The health
task died silently mid-handoff: no log line, no "exception never retrieved"
warning (cancellation is normal asyncio), and no retry. The platform stayed
stranded until the gateway was restarted by hand.

Fix: dispatch the notification onto a new task, the same pattern
DiscordAdapter._handle_bot_task_done already uses for this reason. disconnect()
can then cancel the health/supervisor task freely without that cancellation
reaching the code still running the handoff, so the handoff always reaches the
reconnect queue. Both Photon fatal call sites are converted: the health-poll
path (observed wedging) and the sidecar-crash path (same shape, not yet
observed). gateway/run.py is untouched.

Observed twice on a self-hosted gateway, ~4h38m and ~52min of silent inbound
outage, both cleared only by a manual restart, both post-dating #69112's merge.
In each case the fatal log line appears and `queued for background reconnection`
never does.

Tests: new tests/plugins/platforms/photon/test_fatal_notify_self_cancel.py
covers the self-cancellation (fails with CancelledError without this change),
that the dispatch does not block its caller, that a failing notification warns
rather than raising, and a source guard against reintroducing either inline
await. Two assertions in test_overflow_recovery.py that drove these coroutines
directly now drain pending tasks before asserting delivery, since the
notification is deliberately no longer awaited inline.

Prepared with agent assistance and reviewed before submission.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 21:45:52 -07:00
..
buzz feat(buzz): WebSocket inbound transport — NIP-42 auth, live DM discovery, poll fallback 2026-07-28 21:45:34 -07:00
dingtalk fix(dingtalk): don't let richText re-derivation clobber VOICE classification 2026-07-28 14:06:56 -07:00
discord refactor(discord): delegate ffmpeg discovery to shared tools.transcription_tools helper 2026-07-28 11:56:37 -07:00
email fix(email): harden adapter against malformed IMAP responses 2026-07-02 03:12:53 -07:00
feishu fix(feishu): classify native voice messages as VOICE for auto-transcription 2026-07-28 14:06:56 -07:00
google_chat fix(gateway): extend the utf-8 file-I/O guard to google_chat + whatsapp 2026-07-24 15:47:12 -07:00
homeassistant fix(gateway): validate multiplex adapter config by platform 2026-07-16 05:39:58 -07:00
irc fix(telegram): preserve Bot API update queue on watcher reconnect 2026-06-25 21:29:57 -07:00
line fix(line): normalize inbound media types and cache routing 2026-07-28 14:06:56 -07:00
matrix fix(tts): improve Opus encoding quality for voice messages 2026-07-28 11:55:48 -07:00
mattermost fix(platforms): clear home channel when setup prompt left blank 2026-07-23 12:01:24 -07:00
ntfy fix(telegram): preserve Bot API update queue on watcher reconnect 2026-06-25 21:29:57 -07:00
photon fix(photon): dispatch fatal-error notification from a detached task 2026-07-28 21:45:52 -07:00
raft fix(security): widen non-ASCII compare_digest crash fix to all sibling sites 2026-07-16 07:22:24 -07:00
simplex fix(telegram): preserve Bot API update queue on watcher reconnect 2026-06-25 21:29:57 -07:00
slack fix(slack): prefer live send adapter and try multi-workspace tokens individually 2026-07-23 12:01:24 -07:00
sms fix(security): widen non-ASCII compare_digest crash fix to all sibling sites 2026-07-16 07:22:24 -07:00
teams fix(security): pin DNS resolutions for SSRF-safe fetches 2026-07-23 11:44:43 -07:00
telegram fix(telegram): render markdown in voice-message captions 2026-07-28 11:55:48 -07:00
wecom fix(platforms): clear home channel when setup prompt left blank 2026-07-23 12:01:24 -07:00
whatsapp fix(whatsapp): preserve voice notes when STT fails 2026-07-28 14:06:56 -07:00