Compatibility
You will upgrade the host more often than you upgrade your client. This page says what keeps working when you do, one surface at a time, and — where a guarantee does not exist yet — says that plainly instead of leaving you to infer one.
The wire protocol
Section titled “The wire protocol”The wire evolves by addition. A newer host may add methods, notifications, error kinds, and optional fields. A client written against an earlier version of the protocol keeps working against it.
Three rules follow from that, and they are the ones worth building into your client:
- A schema fingerprint you do not recognise is a warning, never an error.
The handshake result carries
schema.fingerprint, and the TypeScript SDK compares it to the one it was built against and setsfingerprintWarningwhen they differ. Surface it if you like; do not refuse to run on it. Treating a mismatch as fatal turns every routine host upgrade into an outage for your users. (The Python SDK is the one deliberate exception, for its unbundled-host install story — see below.) - Treat every enumerated value as an open set.
turn/completedcarries aterminalthat is"completed","cancelled"or"failed"today, and the disposition on aturn/startack is"started","queued"or"steered"today. A future host may send a value neither this page nor your code knows. Handle the ones you act on and treat anything else as its category’s default — an unknown terminal is still terminal. - An unknown member is not an error. Read the fields you need by name and ignore the rest, rather than validating that a frame contains exactly what you expected.
Changes that addition alone does not cover are the exception, and each one is listed on the protocol changelog. That page is generated from the record such a change has to be entered in before it can reach the schema, so it cannot fall behind the protocol.
While the protocol is before its 1.0 release, no removal window is owed: a surface can change without first shipping a deprecation notice. From 1.0 onward, removal follows a notice in an earlier release, and the changelog lists each completed one.
The SDK
Section titled “The SDK”TypeScript
Section titled “TypeScript”The SDK pins the fingerprint it was generated against and compares it at
handshake, which is where fingerprintWarning comes from. It does not refuse
a host it does not recognise, and neither should you.
An older SDK against a newer host is the supported direction, and it is the direction that follows from additive evolution: fields the SDK does not know about are carried through untyped rather than rejected. The reverse — a newer SDK against an older host — is not a supported configuration. The SDK may call a method that host does not implement, and you will see the error for an unknown method rather than a graceful degradation.
Python
Section titled “Python”The Python SDK (muse_code, distribution muse-code-sdk) carries the same
fingerprint pin with the opposite posture, deliberately: it fails at
initialize when the served fingerprint differs from the pin, with an
exact MuseHostMismatchError naming the required host version and pointing
at this page. The wheel does not bundle a host — you pair an installed
muse with an installed SDK yourself — and the strict gate is what makes a
wrong pairing loud at connect time instead of undefined behaviour later.
Seeing that error means the pair is mismatched; update either side to a
matching pair.
The row below is generated from the SDK source tree with every docs build, never written by hand:
| Surface | Value |
|---|---|
| Packages | muse-code-msp (import muse_code_msp), muse-code-sdk (import muse_code) |
| Supported Python | 3.10 and newer (>=3.10) |
| Runtime dependency | pydantic==2.13.5 (exact pin; the SDK's only one — the wire-types package has none) |
| Schema fingerprint (stable bundle) | sha256:7469c9e352e67def4a59df7e439984d7194fa351e1c8b7abb34060fd977ced81 |
| Schema version | 1 |
| Wheel | Host version | Schema fingerprint | Python |
|---|---|---|---|
muse-code-msp 1.3.0 | 1.3.0 | sha256:ab69549a7ebb423fce94068762da0b5ff3cdec1f8fc263dcc17248eda117f852 | >=3.10 |
muse-code-sdk 1.3.0 | 1.3.0 | sha256:ab69549a7ebb423fce94068762da0b5ff3cdec1f8fc263dcc17248eda117f852 | >=3.10 |
Sessions already on disk
Section titled “Sessions already on disk”The compatibility window for persisted sessions across host upgrades is a
stated contract, and it is unbounded backward. Every session written by an
earlier released host stays openable and servable by a newer one: session/resume
must reconstruct the transcript and state its durable record supports. There is
no vintage cutoff, no “N releases back”. The one recorded carve-out is a narrow
dev-stage vintage that wrote an early flat form of the tool-search sidecar,
before its current versioned form; it loses in-flight continuation — typed when
it applies — but not its history. Any future
narrowing of this window would be an explicit, stated contract change, never a
silent behavior shift.
A stated window does not mean history omission can never happen — it means omission is typed, and your client has to handle the shape it arrives in:
session/resumereturns ahistorywhosemodemust be read beforeitems. For a short session it is{ mode: "inline", items: [...] }. For a long or compacted one you get a snapshot withitems: null.mode: "none"is a state your client must be able to render, and it now says why. It means the host is not serving history for this session, and it carries anoneReasonmember naming which producer omitted the history — an open enum whose current values are"excluded","cursorSuffix","historyBudget","projectionUnavailable", and"projectionReadLimit". Treat it as open: render unknown values generically. The values split by recovery:historyBudgetandprojectionReadLimitmean the history still exists — page it in viaview/page;excludedandcursorSuffixhonor your own request; onlyprojectionUnavailableis unrecoverable by paging. A current host never serves a bare, unexplainedmode: "none"— that would be a host defect — but an older host predating this member omits it; treat an absentnoneReasonthe same as an unknown one.- The session itself still resumes.
session/resumereturning no history does not mean the session is unusable —resumed.session,resumed.viewCursorandresumed.pendingRequestsare still yours to work with.
Build for that: when history is genuinely unavailable (projectionUnavailable),
show the user a session whose earlier turns cannot be displayed — with the
reason the host gave — rather than treating it as corrupt or hiding it; for the
pageable reasons, fetch before concluding anything is missing. A client that
assumes history is always available will show an empty conversation where there
was a real one — and one built from the omission case alone will permanently
hide history that a single view/page call recovers.
Where to go next
Section titled “Where to go next”- Protocol changelog — the non-additive changes, and the deprecations, in full.
- Resume — the cursor and history contract in
detail, including what each
history.modemeans for your UI. - MSP concepts — sessions, turns, approvals and exit classification as one model.