In this line:
val animals = ArrayBuffer.empty[Any]
// ↑↑↑
You explicitly tell Scala that your animals ArrayBuffer can contain Any object. As a result, you can only use methods of Any which are not that many.
It is, for example, perfectly legal to put an Int into animals, since you told Scala that the contents of animals can be anything. However, Int doesn't have a name method, and thus your code would blow up at runtime if you tried to get the name of an Int. In order to prevent your code from blowing up at runtime, Scala rather decides to not allow you to write that code at all in the first place.
So, you need to tell Scala what kinds of things you want to put into the animals. Unfortunately, we only have two explicit nominal types at our disposal here: Dog and Cat. However, when you type animals as ArrayBuffer[Dog], then you can't put Sally in there, and if you type it as ArrayBuffer[Cat], then you can't put Harry in there.
So, we need to type animals as something that allows both Dogs and Cats.
In Scala 3, you could use a Union Type:
val animals = ArrayBuffer.empty[Dog | Cat]
Alas, Scala 3 is still quite far away.
Another way would be to use a Compound type with structural refinement:
val animals = ArrayBuffer.empty[{val name: String}]
This allows your code to work, but it may not necessarily do what you want: this allows any object that has a val named name of type String, not just Dogs and Cats. In particular, you could put something in animals which is not an animal, as long as it has a name.
The best way would probably be to introduce an abstract supertype (let's call it Pet) that defines an abstract val name that gets overridden by Cat and Dog, and then type animals as ArrayBuffer[Pet]:
trait Pet {
val name: String
}
class Dog(val name: String) extends Pet
class Cat(val name: String) extends Pet
val animals = ArrayBuffer.empty[Pet]