Protocol Version
PROTOCOL_VERSION on the wire vs SDK semver — when each changes and how breaking changes are signaled.
There are two independent version numbers in Cosmo Realtime: the wire protocol version and the SDK package semver. They change for different reasons.
Wire protocol version
EXTERNAL_PROTOCOL_VERSION = "1.0" is a string constant defined in backend/app/models/external_realtime_protocol.py and stamped on two frames:
| Message | Field | Direction |
|---|---|---|
session-config | version | client → server |
RealtimeReady | version | server → client |
RealtimeReconnecting | version | server → client |
The server checks the client's version on session start. If it does not match, the server sends:
{
"type": "error",
"code": "version_mismatch",
"message": "Client protocol version '0.9' is not supported. Expected '1.0'.",
"fatal": true
}The session is terminated. The client must upgrade its SDK.
What counts as a breaking change
A change is breaking (increments the protocol version) if it:
- Removes a field that the peer requires to be present.
- Changes the meaning of an existing field.
- Renames or removes a message type.
- Changes the discriminator literal on any message.
A change is non-breaking (no version bump) if it:
- Adds an optional field with a safe default.
- Adds a new message type (the peer can ignore unknown types).
- Fixes a documentation error.
SDK semver vs protocol version
| Dimension | Version | Changes when |
|---|---|---|
| Wire protocol | "1.0" (string) | Schema-breaking change to any wire message |
cosmo-ai | 1.x.y (npm semver) | New features, bug fixes, internal refactors |
cosmo-ai-sdk | 1.x.y (PyPI semver) | Same |
CosmoRealtime | 1.x.y (Swift package semver) | Same |
An SDK minor version bump does not imply a protocol version bump. An SDK may ship new hooks, helpers, or performance improvements with no wire change.
A protocol version bump always comes with an SDK major version bump — the new SDK is the first one that speaks the new protocol.
Compatibility table
| Server protocol | SDK version | Outcome |
|---|---|---|
1.0 | cosmo-ai@1.x | Compatible |
1.0 | cosmo-ai@0.x (hypothetical) | version_mismatch error, fatal |
Future 2.0 | cosmo-ai@1.x | version_mismatch error, fatal |
Checking the protocol version at runtime
TypeScript
import { PROTOCOL_VERSION } from 'cosmo-ai';
console.log(`Client speaks protocol ${PROTOCOL_VERSION}`); // "1.0"The ready event does not expose the server's protocol version on the ReadyEvent type. The raw RealtimeReady wire message has a version field, but the SDK normalizes it away. You can access it via the catch-all transport message listener if you need the server-side value at runtime; the PROTOCOL_VERSION constant above is what the client sends and what the server is guaranteed to match (otherwise the session is rejected with version_mismatch).
Python
from cosmo_ai import RealtimeReady
async for event in session:
if isinstance(event, RealtimeReady):
print(f"Server protocol version: {event.version}") # "1.0"The Python SDK does not export its own version constant publicly — read the
server's off the ready event.
Swift
import CosmoRealtime
print("Client speaks protocol \(RealtimeSession.protocolVersion)") // "1.0"
for try await event in session.events {
if case .ready(let ready) = event {
print("Server version: \(ready.version)")
}
}