Based on the solutions you propose, it seems to me that you're not really leveraging the microservice model effectively, instead being content with what is commonly referred to as a distributed monolith.
Distributed monoliths aren't great. They give you all the drawbacks from monoliths (tight coupling) and all the drawbacks from microservices (increased development overhead) with none of the benefits.
Can anyone think of other possible solutions that I am not considering?
Contrary to what you're expecting, I'm going to instead start excluding solutions that you currently consider to be viable.
Accept the current position and allow applications to access each other's databases as needed
I mean, if not solving problems is an acceptable outcome to you, sure. But then the question is mostly moot to begin with.
Follow a 1 database <--> 1 application model and have the applications communicate via API when they need to get information from each other
This is the microservice way.
Create dedicated code libraries / packages for database access, which are consumed by our applications
This doesn't solve the core issue. It gives you reusability of your logic, but when that logic gets updated it either forces you to update all services at the same time (thus maintaining a monolithic runtime) or your services are going to be running different versions of this package concurrently, which is going to massively complicate how you intend to serve this database in a version-friendly manner.
Move toward a mono-repo solution where multiple applications share a single repo. Build modules within that repo to be responsible for database access
A monorepo is concerned with where you store your code. It has no bearing on the runtime of the services.
Essentially, we get to the same argument as the previous point. While it enables code reusability, it ties your service runtimes together to the point of defeating the benefits from having microservices in the first place.
Investigate using a message bus of some kind.
I'm not even sure what you mean here, but given you point out a lack of knowledge I'm going to assume that this suggestion was a stab in the dark to begin with.
My goal with this question is to have a full list of reasonable approaches I could take, so I can consider the pros and cons and pick an approach.
The core issue here is one of communication between services. The question you should be focusing on is why you have multiple service in the first place. There's two reasonable outcomes to that consideration:
- You didn't need to have separate services
The answer here is to move back to a monolith and deal with it as it is. As much as the world is moving towards microservices, monoliths still have their use cases. At the end of the day, monoliths outperform microservices in per-request performance (by which I mean the resolution time of a single request) and they are organizationally easier to manage.
- You need separate microservices
The answer here is to actually leverage the benefits from microservices, which would inherently make it obvious that a lot of the options you considered to be viable would not be compatible with a microservice architecture (without losing the benefits of having it in the first place).
The microservice way is to define individual services for individual contexts. The underlying components that a service may or may not use, such as a persistence store, cache, or any external connections it relies on, are a private implementation detail that is hidden from sight from all the other microservices. Effectively, the microservice needs to be king of its own domain to ensure that it can change its implementation as it sees fit, without needing to first consider that other services might be making direct use of its private resource (which they simply shouldn't).
I know this answer somewhat sidesteps your concrete question, but you need to consider the bigger picture here as there are some inconsistencies on that level and answering those will inherently help answer questions you may have about the smaller picture.