The 50ms retry loop in #scheduleBackgroundExchangeFlush had no guard
against session teardown. If dispose() was called while streaming,
the setTimeout callbacks kept firing and attempted emitExternalEvent
on a disconnected agent.
- Add #isDisposed flag; set it at the top of dispose() before any
async teardown so poll ticks that fire mid-teardown bail out cleanly
- Clear #pendingBackgroundExchanges in dispose() to drop queued
messages that will never be rendered
- Widen the attempt() bail condition to include #isDisposed so the
loop terminates even if new messages arrived between the dispose()
clear and the next tick