Make all the \s*? greedy (especially the last one which is the culprit):
/(@|at)?\s*(\d+)\s*(am|pm|AM|PM)?\s*-\s*(\d+)\s*(am|pm|AM|PM)?/
^
See the regex demo
The point is that (\d+)\s*?(am|pm|AM|PM)? matches and captures 1 or more digits with (\d+), then the regex engine tries to match (am|pm|AM|PM)? pattern, not \s*?, because \s*? is a lazily quantified atom, and is thus skipped at first. The (am|pm|AM|PM)? pattern can match an empty string, and it does. It matches the empty string right after the digits, and the regex engine calls it a day returning a valid match.
?from all\s*?.