Earlier quoted context omitted.
I did use agents in the past, but stopped since core.async became available: it addresses the use cases for agents in a much more flexible way. Also, I found that handling errors in agents is difficult. As for the general use case, atoms and core.async are great tools, and I haven't needed to use refs (aka the STM) for years.
Don't the core.async go-blocks have issues if you need to block on IO heavy stuff, as in the thread-pool will be blocked until the side-effects are done? Agents don't have that problem, at least I don't think they do.
In addition to this, I personally find core.async to deal poorly with flow control (e.g. slow subscribers slowing down the entire flow for every subscriber), and seems to not have a lot of telemetry / it’s difficult to find out what’s going on in the thread pool behind it.
I’ve personally settled on Zach Tellman’s Manifold library, which provides a much more sane abstraction and is fully compatible with core.async.
For some addition thought and material on these topics, I highly recommend watching this talk by Zach, “Everything Will Flow” https://youtu.be/1bNOO3xxMc0