You are absolutely correct. If you hash or encrypt the password on the client, then pass the resulting digest over an un-encrypted channel, then any man in the middle can simply replay the digest value and log in. The original password is irrelevant now.
What you have effectively done is to turn the digest itself into the password.
The real issue is that you are not using HTTPS to protect your session. The underlying issue is a trustworthy exchange of encryption keys between the client and the server (an offline component of the encryption). This is the exact issue that HTTPS (SSL/TLS) addresses. I'm afraid that by doing your password encryption/hashing on the client and passing the resulting values over a plaintext channel, you're doomed to create a system that is either vulnerable to replay attacks, or too expensive to implement and maintain as a secure system into the future.
Why is HTTPS not an option? If security is the goal, I think HTTPS is probably your only reasonable option.
Your general approach was immortalized in an official Internet RFC 20 years ago, and has been known to be kind of broken for a very long time. Just read about HTTP digest authentication: https://tools.ietf.org/html/rfc2617
Regarding appending a session id... First, what does that matter if the channel is unencrypted and the attacker can just replay the digest (unless you are changing the session id on every call)? Also, there is is no magical session id--if the id is known to the server, then either the server or the client created it and passed it across the wire. So the session id isn't a secret.
To protect this kind of scheme at all, you need to use a nonce. You need to send your encrypted authentication package with every request, and you need to increment the nonce and re-encrypt for every call. That way, the server can see that the nonce is larger than it was on the last request, and thus reject a replay. So now the server has to track that nonce value, per user, in addition to the session id. That becomes more complicated if you have a web garden or server farm, because you have to update that shared state centrally, and that can become a scalability and concurreny issue for your system. And there are still more issues with a scheme like this, which is why one of the factors in the Kerberos authentication schema (for example), is that the server and the client must have their clocks synchronized within 5 minutes of eachother. Among other things, a simple incrementing nonce can leave you vulnerable to attackers with a lot of resources.
You're also doing this in JavaScript in your client, communicating over an unencrypted channel, thus making your system even more susceptible to things like script injection, meaning that some rogue JavaScript in the page could read out values of variables in your security script, including the value of the original password, and phone that information back home to whoever is listening. Do you completely trust every analytics script you include in your pages? Do you completely trust that some new XSS attack can't put a bad guy's JavaScript in your page?
And so on. If you want actual security, you need to implement something more in the league of Kerberos (which is heady stuff), and you probably need to implement it as a browser plugin to provide a bit of a wall between it and other scripts and plugins.
Or you just need to implement HTTPS, and send the password through the encrypted channel to the server, where the server will salt it, hash it (hopefully using an algorithm like scrypt, bcrypt or pbkdf2) and compare it to the digest you have stored on the server.
Otherwise, I'm afraid you're not accomplishing real security.