To me it’s distracting to think about duplicating vs creating an abstraction, because the answer is always “it depends”, which is not really an answer.

To me, the question is: can you look at this abstraction and understand why it exists, without knowing who’s calling it? If so, it’s probably fine.

If an abstraction only makes sense because of the particular weird details of these 3 callers that have to pass mutually exclusive arguments to it to get their desired behavior, it’s probably wrong. An abstraction needs “a place to live” in your architecture. It needs to be self-evident in justifying its existence.

If you find yourself repeating code, but de-duping it would create these sort of weird non-self-justifying abstractions, your architecture is probably a bad fit for the problem you’re trying to solve. Maybe that’s because the problem changed since the software started (which is a bit of a pickle: do you re-architect, or do you continue writing weird inscrutable code?) or maybe it’s because you just picked the wrong abstraction in the first place. But you should recognize it: duplicating vs wrong-abstraction is about choosing the lesser evil. If the abstraction was a natural fit for the problem, you wouldn’t need to answer this question in the first place.