Version
Primary handles
MeerkatClientowns the runtime process and transport.Sessionis a live multi-turn handle.DeferredSessionis a created session that has not run its first turn yet.LiveChannelis the session-bound wrapper for thelive/*RPC surface.Mobis a first-class mob control handle.
Session result and metadata types
RunResult
Returned by session.turn(), deferred.start_turn(), and available as events.result after streaming.
Fields:
session_id: strtext: strturns: inttool_calls: intusage: Usageterminal_cause_kind: str | Nonesession_ref: str | Nonestructured_output: Any | Noneextraction_error: ExtractionError | Noneschema_warnings: list[SchemaWarning] | Noneskill_diagnostics: SkillRuntimeDiagnostics | None
SessionSummary
Returned by client.list_sessions(...).
session_id: strsession_ref: str | Nonecreated_at: intupdated_at: intmessage_count: inttotal_tokens: intlabels: dict[str, str]is_active: bool
SessionDetails
Returned by client.read_session(session_id).
session_id: strsession_ref: str | Nonecreated_at: intupdated_at: intmessage_count: intlabels: dict[str, str]is_active: boolmodel: strprovider: strlast_assistant_text: str | Noneresolved_capabilities: ResolvedModelCapabilities | None
SessionInfo
Base shared metadata type for session identity/timestamps/state.
Usage
input_tokens: intoutput_tokens: intcache_creation_tokens: int | Nonecache_read_tokens: int | Noneaccounting: ProviderTokenAccounting | None
ProviderTokenAccounting
provider: strmodel: strpresented_tokens: intconvention: straggregation: str
Usage type serves two different accounts. When present on the
TurnCompleted event it is per-call usage: input_tokens is the raw provider counter (for Anthropic,
uncached input only) and accounting names the resolved provider/model plus the
normalized presented_tokens for that call. On RunResult.usage and the
RunCompleted event it is the session-cumulative total: input_tokens is
already the sum of every recorded call’s presented_tokens, it is persisted with
the session so it keeps growing across turns, and accounting is None because
a session may span models.
Do not sum the cumulative value with anything - take the latest one - and sum
accounting.presented_tokens rather than per-call input_tokens. Every
committed agent-loop call publishes a turn_completed row; extraction requests
publish theirs on the extraction outcome event, and compaction summaries and
turns that fail after the provider answered publish none, so the
turn_completed rows need not reconcile with the cumulative total. See
Usage accounting for the worked example.
TurnCompleted.usage is Usage | None. None is honest unmeasured
accounting, not a zero-token call. Skip the row; the paired
turn_usage_accounting_unmeasured wire event carries the reason.
Config API contract
get_config returns ConfigEnvelope; set_config and patch_config return ConfigWriteResult:
ConfigEnvelope fields:
config: dict[str, Any]generation: intrealm_id: str | Noneinstance_id: str | Nonebackend: str | Noneresolved_paths: dict[str, str] | None
ConfigWriteResult carries the same fields plus live_propagation: WireLiveConfigPropagationReport | None, the typed per-channel hot-swap / refresh / close outcome (swapped, skipped, swap_failed, refreshed, closed, refresh_failed, close_failed).
Models and schedules
Models catalog
contract_version: {"major": int, "minor": int, "patch": int}providers: list[ProviderCatalog]
Schedule wrappers
Public methods:create_schedule(request)get_schedule(schedule_id)list_schedules(labels=None, limit=None, offset=None)update_schedule(request)pause_schedule(schedule_id)resume_schedule(schedule_id)delete_schedule(schedule_id)list_schedule_occurrences(schedule_id, include_terminal=None)list_schedule_tools()call_schedule_tool({"name": "...", "arguments": ...})
ScheduleListResultScheduleOccurrencesResultScheduleToolsResult
ScheduleRecord is the canonical flattened RPC object: configuration fields
such as planning_horizon_days, created_at_utc, and labels are top-level;
there is no nested config or public machine_state. The wrappers reject a
missing/malformed schedules, occurrences, or tools array and reject schedule
entries missing required identity, policy, planning, or timestamp facts.
WorkGraph wrappers
WorkGraph is a host-observation surface in the SDK. Useget_workgraph_item, list_workgraph_items, list_ready_workgraph_items,
get_workgraph_snapshot, list_workgraph_events,
get_workgraph_goal_status, and list_workgraph_attention. Agents own graph
mutation through their WorkGraph tools.
Session runtime input helpers
Immediate creation and turn methods accept host-owned context separately from the user prompt:injected_context: list[ContentInput] | Noneis ordered durable user-channel context immediately before the prompttransient_turn_context: str | Noneis request-only host context- turn methods also accept
self_hosted_server_idfor an exact configured local model route
injected_context for its eventual first turn but
does not accept transient_turn_context; pass transient facts to
DeferredSession.start_turn(...).
inject_context
send_external_event
ExternalEventOutcome (runtime admission outcome payload).
Transcript and input reconciliation
Additional client wrappers
The Python SDK also exposes:- realm helpers:
list_realms(),get_realm(...) - auth helpers:
list_auth_profiles(...),get_auth_profile(...),create_auth_profile(...),delete_auth_profile(...),auth_login_*,auth_provision_api_key(...),auth_status(...),auth_logout(...) - blob/skill/MCP helpers:
get_blob(...),list_skills(),mcp_add(...),mcp_remove(...),mcp_reload(...) - comms helpers:
session.send(...),session.peers() - typed ingress helper:
send_peer_response_terminal(...) - runtime host inspection:
get_runtime_host_info(),get_runtime_host_capabilities(),get_runtime_host_health()
auth_binding or a
mob member spec instead of embedding provider secrets. A remote member host
resolves the declared binding in its authorized realm.
Detached jobs and monitors
The public job wrappers arejobs_get, jobs_list, jobs_cancel,
jobs_progress, jobs_result, jobs_artifacts, jobs_retry, jobs_health,
jobs_subscribe, and jobs_unsubscribe. monitors_start creates a detached
monitor job. MobKit worker implementations additionally use the six
mobkit_job_* authority-checked mutation methods.
Every public Python RPC wrapper binds its request and result transport boundary
to generated RPC-schema contracts. The job request and result dataclasses in
0.8.24 are available from meerkat.generated.types, but are not re-exported
from meerkat:
Approvals, artifacts, and projected events
- approvals:
request_approval,list_approvals,get_approval,decide_approval - artifacts:
list_artifacts,get_artifact,download_artifact - cursor-based event recovery:
latest_event_cursor,list_events_since,event_snapshotwhen host event projection is enabled
Mob profile and events surfaces
Multi-host operator surface
The client exposes host inventory and route installs, host bind/revoke, grant/revoke/list scopes, remote member history, hard cancel, and remote live channel control. These JSON-RPC methods are trusted operator controls. Public REST/MCP and agent-callable tools intentionally expose narrower read or member surfaces, and agent spawn cannot authorize the Rust-onlyresume_from_role
durable role migration.
Mob event history
MobEventsResult.
Realm profile CRUD
MobProfile,MobProfileToolsStoredMobProfileDeletedMobProfile
Streaming
EventStream is returned by session.stream(...) and client.create_session_streaming(...).
- synchronous constructor style (
session.stream(...)returns stream object) - request is sent on
async withentry events.resultis available after iterationBackgroundJobCompleted.terminal_statusis the typed terminal status for background jobs (job_id,display_name,terminal_status,detail)
Live channels
RealtimeChannel and the old realtime convenience helpers are no longer public
SDK handles. The current helper is LiveChannel.session(client, session_id, ...),
which binds a session id and stores the channel_id returned by live/open.
MeerkatClient helper names mirror the RPC methods:
live_open(session_id, turning_mode=None, transport=None, seed_max_chars=None)live_webrtc_answer(channel_id, token, offer_sdp)live_status(channel_id)live_close(channel_id)live_send_input_text(channel_id, text)live_send_input_audio(channel_id, data_base64, sample_rate_hz, channels)live_send_input_image(channel_id, idempotency_key, mime, data_base64)live_send_input_video_frame(channel_id, codec, data_base64, timestamp_ms)live_commit_input(channel_id, response_modality=None)live_interrupt(channel_id)live_truncate(channel_id, item_id, content_index, audio_played_ms)live_refresh(channel_id)
live_send_input_image requires a caller-stable, session-scoped key. Its
return value proves queue acceptance only; durable success is the later
user_content_committed observation carrying that same key.
Pass a positive seed_max_chars to bound serialized seed messages and request
a core-owned whole-turn suffix at open time. Omission preserves the complete
seed; zero is rejected. Every ordered System message must fit; an existing compaction
summary may be retained, and any truncation reports degraded continuity.
Canonical image identity, tombstone, and accounting sidecars remain complete.
live_open returns a discriminated transport object. The websocket variant
has url and token. The webrtc variant has token, answer_method, and
an optional http_url; create an SDP offer and pass it to
live_webrtc_answer. Do not assume every transport has a URL.
WebRTC requires connect(live_webrtc=True) and an rkat-rpc binary compiled
with its non-default live-webrtc Cargo feature. The 0.8.24 auto-downloaded
release binary does not include that feature. Add an audio track or the
meerkat.live data channel, set the browser peer’s local description, wait for
ICE gathering to complete, then send peer.localDescription.sdp. The protocol
has no candidate-trickle RPC. Install the returned answer_sdp as the browser
peer’s remote description before sending live input.
Event compatibility note
The generated event inventory in 0.8.24 knowsreasoning_delta,
reasoning_complete, assistant_image_appended,
turn_usage_accounting_unmeasured,
turn_usage_accounting_identity_disputed, interaction_callback_pending,
peer_content_ingested, and provider_cache_breakpoints_discarded. The
handwritten parser has no typed case for these eight rows, so they arrive as
UnknownEvent.
Current Rust server_tool_content and
transcript_rewrite_audit_receipt_committed are absent from that generated
inventory. Receiving either through this parser raises UNKNOWN_EVENT_TYPE.
Current run_started, run_failed, retrying, and hook_failed use newer
field shapes than the handwritten parser and can arrive as malformed events.
Treat all of these as SDK/code-generation gaps, not stable alternate wire
contracts.
In the current 0.8.40 SDK, model_fallback_skipped,
model_fallback_staged, model_fallback_committed, and
model_fallback_target_failed are also absent from the generated inventory.
If a runtime emits one during model fallback, receiving it raises
UNKNOWN_EVENT_TYPE rather than yielding UnknownEvent. Streams that do not
receive these events are not affected by this particular gap.
Capability and error model
Capabilities:MeerkatError.