It is not perfect yet, and especially how tokio does not follow the rest of the ecosystem by implementing their own traits is kind of disappointing. We can work around that, but I was hoping they would fix this by version 1.0...
Another way to be runtime agnostic is to start a particular runtime as part of your library, which is used internally. The public interface of your library can provide async functions which are agonstic to a particular runtime, since all actions will be deferred/forwarded to an internal runtime. That approach has a bit more overhead, but can ease usage.
I understand the reasoning for not wanting this in the core language, but perhaps there could be some standard implementations which would still allow for custom runtimes.
That said, the last time I looked at it, Tokio was much too complex for my tastes.
If you want to dig deeper:
(Some of those interoperation points are still being worked out, so it's not perfect yet.)