The Android client had no way to see or refresh the peer's SSO session:
the core tracked the deadline and published expiry warnings, but none of
it was exported, so an expired session surfaced only as a raw error
string from the engine run loop.
Mirror the surface the daemon serves its tray:
- Status() and SessionExpiresAtUnix() report the run-loop status label
and the tracked deadline, the two values StatusResponse carries.
- StateChangeListener signals state changes (payload-free, consumers
re-read the getters) and forwards the session-expiry warnings from
the engine's watcher, filtered out of the shared event stream.
- ExtendAuthSession() runs the interactive SSO flow and refreshes the
deadline without touching the tunnel, with CancelExtendAuthSession()
for an abandoned browser round-trip — its PKCE wait would otherwise
hold the loopback port until it timed out and block every retry.
- DismissSessionWarning() suppresses the final warning.
Status() latches NeedsLogin: the run loop keeps its status in a
per-run context state that a restart replaces with a fresh Idle one, so
an engine restart would otherwise erase the fact that the peer still
needs to log in. Only a successful login or extend clears it.