How to upgrade
Meerkat is pre-1.0, and patch releases can carry declared breaking changes.- Keep everything on the same patch version. The SDK handshake accepts
any
rkat-rpcwith the same major and minor version, so a mismatched patch connects without complaint. Upgraderkat-rpc,meerkat-sdk,@rkat/sdkand@rkat/webtogether. - Bump Rust pins together. The
meerkat*crates are versioned in lock step: move every=0.8.xpin at once. - Expect one-way migrations. A release can migrate durable state forward the first time it opens it, after which older binaries refuse it. Each release section below says whether it does.
Steps
- Stop every process that shares the state root: the CLI,
rkat-rpc,rkat-rest,rkat-mcpand SDK applications. - Back up the realm state root. Without a backup, a release that migrates durable state cannot be rolled back.
- Run
rkat storage doctor(read-only) and keep its report. - Upgrade the binaries and SDKs (table below).
- Start the new version. It migrates durable state on first open. To
migrate offline instead, run
rkat storage migrateas a dry run, then with--apply(see Storage Operations). - Run
rkat storage doctoragain and compare. - Once the rollback window has passed, remove migration backups with
rkat storage prune.
Verify
rkat --versionandrkat-rpc --versionprint the new version.rkat doctorpasses.rkat storage doctorreports no new problems.- An SDK
connect()succeeds.
Upgrading to 0.8.51
This section is for systems running 0.8.50. The full list of changes is in the CHANGELOG under## [0.8.51].
Before you upgrade
- The upgrade is one-way. 0.8.51 migrates these stores forward the first
time it opens them, and 0.8.50 binaries refuse the migrated files:
- SQLite session store: schema v5 (transcript retirements);
- WorkGraph: schema v4 (keyed admissions);
- semantic memory: schema v3 (purges empty-text rows).
- Upgrade every process that shares a store at the same time. A 0.8.50 process left running against a store a 0.8.51 process has migrated will be refused on its next open.
- Take a backup first (step 2 of How to upgrade), since there is no downgrade path for migrated files.
SQLite sessions that 0.8.50 could no longer load
SQLite sessions written by 0.8.50 or earlier that went through more than 32 compaction rewrites could reach a state where every cold load fails withStore(Corrupted(<session id>)). Writes kept succeeding and no rows were
lost.
- Do not delete or recreate such a session. Upgrade the host to 0.8.51.
- 0.8.51 loads these sessions again with no action needed. Cold load replays them from the edge their rotated anchor sealed, but only after verifying that the edge’s result, extended by the anchor’s own tail rows, reproduces the anchor exactly. The session’s next compaction rewrite then re-mints the anchor.
- New sessions are not affected.
Session envelope status
- No envelope migration. The persisted session envelope stays at version
3 (
SESSION_VERSION), stored input state at version 5, and session metadata schema at version 2, the same as 0.8.50. Session documents written by 0.8.50 load unchanged. - Transcript history is now bounded. After each compaction, a session
keeps the bodies of only the most recent
[compaction] history_retained_rewritesrewrites (default 4). Existing documents re-anchor on their next compaction. The SQLite store deletes the rewrite rows below the same cut. - Old revisions can be retired. Fork, rewind, restore and revision reads
of a revision older than the retention window fail with the typed
TranscriptRevisionRetired, naming the oldest retained revision. Raisehistory_retained_rewritesif you need to reach further back. Code that walkscommits()and projects each commit’s parent must skip commits before the cut.
Breaking changes and what to do
Hosts that serve application tool policies
- Agent
mob_create, agentdelegateand spawns into existing child mobs are refused (policy_denied, codechild_tool_policy_required) until you choose a child application tool policy. - Do: call
MobMcpState::with_child_application_tool_policy(binding)with the policy child members should run under, or passApplicationToolPolicyBinding::Unmanagedto opt out explicitly. delegatehelpers now run as a child mob under that policy. Host-created mobs, same-mobmob_spawn_memberandfork_off, and temporary councils are unaffected. Hosts without a tool-policy registry see no change.- Host tool bundles reach child mobs only if you mark them
ChildAvailablewithMobMcpState::with_child_tool_bundles.
Agent-facing mob tool inputs
- Model-supplied mob definitions and profiles that name internal-only fields
(
mcp_servers,rust_bundles,is_implicit,session_cleanup_policy, …) are refused withInvalidArguments. On the agent surface, host filesystem skill paths, images byblob_idand videos byuriare refused too. - Do: keep MCP servers and Rust bundles in host-defined profiles, and have agents reference those profiles by name. Host-facing surfaces (RPC, REST, SDK, CLI) are unchanged.
MCP OAuth
- Meerkat no longer opens a browser or binds a callback listener itself.
- Do:
- drive login with
McpOAuthAuthority::login_start,login_completeandlogin_cancel, theHostAuthService::mcp_login_*methods, theauth/login/*RPC and REST methods with an MCP target, orrkat mcp login(see Authentication); - give each server that needs interactive login an
oauth_account; - update
McpOAuthAuthority::new(persistence, auth_lease)andwith_http(persistence, http, auth_lease)calls, which no longer take a browser; - set
AgentBuildConfig::mcp_auth_resolver(useNoneto keep the old behaviour); - replace
BrowserOpener,SystemBrowserOpenerandMcpOAuthAuthority::interactive_login, which are removed.
- drive login with
- RPC, REST and the MCP server now install interactive MCP auth by default.
A missing credential surfaces as the typed
AuthorizationRequiredhost status.
Auth wire (auth/login/*, auth/status/get)
- Login params take a
target(a provider or an MCP server). The RustLoginStartParams,LoginCompleteParams,WireLoginStartandWireLoginReadyreplace their provider fields withtarget. - Provider login params now refuse unknown fields.
- Do: in Python, build requests through the client wrappers; the generated types are unions and no longer constructible dataclasses. Provider JSON payloads themselves are unchanged.
Per-profile tool deny lists
[profiles.<name>.tools] deny = [...]is new. A name that is not a known tool (or a tool of a declared MCP server’s mapped list) fails the member’s build withDeclaredToolUnknown.- A profile’s
read_onlynow travels asdeclared_tool_restrictionand no longer appears inAgentBuildConfig::tool_access_policy. The effective gate is unchanged. - Do: if you read
tool_access_policyoff a built config, readdeclared_tool_restrictiontoo. Members persisted before 0.8.51 keep their old restriction until respawned.
Mob Stop and Shutdown
MobHandle::stopreturnsResult<MobStopReport, MobError>.MobMcpState::mob_stopreturns the report, andmob_lifecycle_actionreturnsMobLifecycleReports.- Shutdown cancels members’ in-flight runs immediately. Use Stop if runs
should finish first.
shutdown_with_report(ShutdownOptions)reports per-member outcomes, andShutdownOptions::with_deadlinebounds every wait by your own deadline (for example below a Kubernetes termination grace period). - While a Shutdown runs, commands other than a second Shutdown get
ActorCommandChannelClosed. Treat it as “mob shutting down”. - A retirement that started and failed answers
MemberRetirementStuck. Do: callMobHandle::redrive_retirementor resume the mob. - A spawn that completes into a Stopped mob leaves it Stopped and holds the
new member. Call Resume to run it.
Resetalso releases a Stop’s holds. MobBuilder::with_runtime_adaptermust be passed the session service’s ownMeerkatMachine(a clone of it). A different machine over the same store is refused withMobError::RuntimeOwnerConflict.
Live (GPT Live and WebRTC clients)
WireLiveAdapterStatus::Closedis now a struct variant,Closed { reason, reopen_recommended }. Do: matchClosed { .. }(or useis_closed()) and build it withWireLiveAdapterStatus::closed(). A plain close still serializes as{"status":"closed"}.AgentEvent::LiveChannelClosedis published on every committed close. Do: handle it in event consumers that match exhaustively on event types.- WebRTC clients should answer
live/media_health_requestedwithlive/media_health, and may applylive/assistant_playback_hint(duck/restore) to their playback. The TypeScript and Python SDKs now deliverlive/*notifications throughonLiveNotificationandon_live_notification. - Hosts that forward observations receive playback hints only if they
override
ExperimentalLivePublicObservationPublisher::publish_playback_hint.
Other behaviour changes
- rkat-rpc over TCP: a session’s callback tools route only to the
connection that created it.
SessionRuntime::set_callback_channelis deprecated. Do: useinit_callback_channelfor the single-client default route. - MCP stdio servers are killed as soon as their stdin closes. A server that must flush state on EOF can no longer rely on a grace period.
- Shell tool commands read EOF from stdin immediately (
/dev/null). - Mob kickoff status notices (
mob.kickoff_*) are one-way lifecycle notices. Receivers no longer get a peer request to answer. - SQLite database paths that are hard links or not regular files are
refused (
UnsupportedDatabaseFile). Do: point stores at a single regular file. - Semantic memory: empty-text rows are purged on first open, and empty queries return no hits.
Rust API
Struct literals and exhaustive matches need updating for the new fields and variants listed under### Breaking in the CHANGELOG. The most common are:
LlmRequest.tool_choice,TurnToolOverlay.tool_choice_plan, andCompactionConfig.history_retained_rewrites(default 4);MemoryIndexExclusion::EmptyText,TranscriptEditError::TranscriptRevisionRetired,LlmError::ToolChoiceUnsupportedandMobError::RuntimeOwnerConflict;SessionRuntimeLlmReconfigureHost::serviceis now aWeak(build it withArc::downgrade).
Upgrade checklist
- Back up stores you might need to roll back.
- Stop every 0.8.50 process that shares a store, then start 0.8.51 on all of them together.
- If you serve application tool policies, set a child application tool policy.
- Move host-only MCP servers and Rust bundles out of agent-created profiles.
- Update MCP OAuth hosts to the start/complete/cancel flow.
- Review Shutdown callers: use Stop to let runs finish, and set a
ShutdownOptionsdeadline that fits your termination grace period. - Update live clients for
Closed { .. },LiveChannelClosed, and the media-health and playback-hint notifications. - Leave any SQLite session that 0.8.50 reported as
Corruptedin place. 0.8.51 loads it.
