How do I minimise boilerplate code and hard dependencies when actor components need to interact with each other?

What’s the issue with some variant of the first method?

From each component that needs to interact with the others, you can get your owning actor’s component-of-specific-class (post-initialization, or at another point) and if it exists, use the reference. I assume this is what you mean by your first point, but you don’t need to cast to any character type, since these functions exist in AActor.