That's not entirely true, OpenSSL also gives you something in the middle - the "Envelope Encryption" functions like EVP_SealInit(). They're just not widely known (and still allow you wide latitude in choosing obsolete primitives).
> the "Envelope Encryption" functions like EVP_SealInit()
That thing is a footgun.
Those functions encrypt a block of data to a public key without authentication. And encourage you to encrypt to more than one public key at the same time, which is generally a bad idea. The example cipher in the man page is EVP_des_cbc().
It looks like the fact that they're not widely known is the good news. :)
It does exactly what it says on the tin - it encrypts something so that only the recipient(s) can access it. It is very far from the low-level primitives: it takes care of generating the symmetric key, implementing the underlying cipher mode (so, for example, if you use a type like EVP_aes_128_gcm() you'll get correctly implemented GCM) and generating the IV. It takes care of padding for the asymmetric encryption step. In short, it protects you from a whole slew of low-level implementation mistakes that await you if you use the low-level primitives.
There's also EVP_Sign() which also does what it says on the tin - ensures the message comes from who it says it does, but anyone can read it. You can compose these together as well, to create a message with both properties.
What they don't protect you from is designing an insecure protocol* on top, but then NaCl doesn't either: for example it's easy to create a protocol with trivial replay attacks. I believe the classes of errors you allude to fall into this category.
Perhaps you think there should be an EVP_Box*() that combines signing and sealing, but regardless it is simply not true that OpenSSL provides nothing between raw AES and full-blown TLS.
> Those functions encrypt a block of data to a public key without authentication.
How is that a problem? I mean, this is not symmetric crypto where the 2 parties are supposed to share a secret session key. While you generally do want authentication, I can imagine a use case for anonymous messages.
Anonymous messages cannot have authentication. Even if they had, the attacker can just block your message and forge another with another public key of his. The receiver won't make a difference, it's a throwaway key to begin with.
The problem is it has the wrong default. The equivalent in NaCl is crypto_box(), which requires the sender's private key to authenticate the message. If you really wanted anonymous messages then you could generate a new private key for each message and immediately throw it away, which gives you a much stronger signal that you're doing something unusual.
What those OpenSSL functions do is generate a symmetric key, encrypt the symmetric key with the public key (to give to the recipient), and then encrypt the data with a symmetric cipher using the symmetric key. Those are each already basic primitives, so all the combined function is really doing is making it easier to do something insecure that most people would rarely actually want.
It's possible to use it correctly, but it's too easy to use it incorrectly. Doing something uncommon with bad security properties should be possible but not easy.
EVP lets you shoot yourself in the foot in various ways. OpenSSL also doesn't provide any API to e.g. validate a TLS cert chain, so everyone does it their own way and often gets it wrong.