keep_aliveis runtime/session behaviorsession/external_eventqueues runtime-backed external work- committed success is not rewritten to cancellation
Getting started
1
Start the server
0.0.0.0:9001 requires --allow-remote;
that flag only opts in to network exposure and does not add
authentication or encryption. Use it behind a production-safe transport
wrapper such as SSH tunneling, mTLS, or another authenticated encrypted
channel. Plain TCP host capabilities continue to report
secure_remote_rpc: false.rkat-rpc --tcp is a JSON-RPC host transport. It is not the signed
Meerkat peer/comms channel used by remote agents or external mob members;
use rkat run --comms-listen-tcp ... for that.2
Send the handshake
3
Create a session
rkat-rpc defaults to a new isolated realm each time. To share sessions with
another process, use the same --realm <id> and physical --state-root, or a
matching --context-root that resolves the same project-local root.Runtime scope
rkat-rpc accepts global scope flags:
--realm: explicit sharing/isolation key--instance: optional instance metadata--realm-backend: creation hint only; actual backend is pinned per realm manifest
--live-webrtc is present only in builds with the live-webrtc feature.
--live-ws-scheme wss changes the advertised bootstrap URL for a
TLS-terminating proxy; the listener itself still accepts plain WebSocket bytes.
Method overview
This table mirrors the generated catalog inartifacts/schemas/rpc-methods.json
(meerkat_contracts::rpc_method_catalog). initialize returns the methods
enabled by both the compiled build and runtime composition. Reduced builds,
an absent skill runtime, or unconfigured live transports can expose a subset
of this full catalog.
Both helper parameter types (
MobSpawnHelperParams, MobForkHelperParams)
require result_label and max_text_bytes. MobHelperResult is the exact
operation carrier: output, tokens_used, agent_identity, member_ref,
bounded_result, session_id, usage, turns, and tool_calls are
required, while retirement_error reports post-result cleanup debt when
present.
Every
Schedule result is one flattened public object. Configuration fields
including planning_horizon_days, planning_horizon_occurrences, labels,
created_at_utc, and updated_at_utc are top-level; there is no nested
config and persisted machine authority is not exposed as machine_state.Generated assistant images use the same surface-neutral history and blob APIs as other blob-backed artifacts. Read
session/history, find assistant blocks with block_type: "image", then call blob/get with data.blob_ref.blob_id to retrieve the base64 payload.Live channels provide low-latency audio/text streaming with model-gated image input. Create a session
with a realtime-capable model (e.g.
gpt-realtime-2), then call
live/open to start the channel. The --live-ws <addr> flag on
rkat-rpc enables the WebSocket listener required for audio transport.
Use live/status to observe channel state and check
live/open’s capabilities.image_in before sending image context.RPC is the canonical typed substrate for the SDKs: use explicit
mob/* lifecycle, host-ingress, and observation methods from apps. Inside running sessions, mob capability is exposed by composing meerkat-mob-mcp (MobMcpState + AgentMobToolSurfaceFactory) into SessionBuildOptions.mob_tools, which provides authorized mob_* tools to the agent. external_tools remains for callback and MCP-backed tool dispatchers.WorkGraph RPC methods are observability reads. Programmatic hosts inspect
WorkGraph through
workgraph/get, list, ready, snapshot, events,
goal/status, and attention/list; agents mutate ordinary WorkGraph state
through the workgraph_* tools. Authority-bearing attention reassignment and
policy escalation are runtime-injected WorkGraph tool operations, not public
JSON-RPC methods.Runtime host projections and health
runtime/host_info is a read-only projection of process identity, host ID
scope, realm and endpoint metadata, feature flags, and health. It does not
enroll a member host or grant mob placement authority.
runtime/capabilities returns a contract version plus boolean
RuntimeHostFeatureFlags: runtime_backed_sessions, mobs, mcp_live,
comms, blobs, session_events, session_streams, schedules, skills,
event_replay, artifacts, approvals, external_members,
secure_remote_rpc, multi_host_mobs, and durable_jobs. These host facts are
distinct from the generic status-bearing CapabilityId entries returned by
capabilities/get. Older payloads that omit multi_host_mobs or
durable_jobs decode those fields as false; clients should use the flag and
not infer multi-host support from a version number.
runtime/health returns the worse-wins rollup across five declared dimensions:
jobs, session_liveness, session_durability, session_runtime_loop, and
session_run_start. This RPC host probes all five. A plain check key is a
measured result. unreadable:<dimension> means a probe ran but could not
obtain a reading and rolls the overall status to at least degraded.
unmeasured:<dimension> is a coverage marker for a surface with no probe and
does not enter the rollup.
The session liveness check covers queued work parked on an active executor with
no run in flight, including repeated staging churn. session_run_start covers
a current staged run that is overdue to begin. Neither claims to measure a run
that began and then stopped producing progress.
Protocol
Standard JSON-RPC 2.0 with"jsonrpc": "2.0" on every message. Three message types:
- Request (either direction): has
id,method,params. The server sendstool/executerequests to clients for registered callback tools. - Response (back to the requester): echoes
idwithresultorerror. - Notification (either direction): has
method,params, noid, and receives no response.
Full lifecycle diagram
Full lifecycle diagram
Request cancellation
Cancel an in-flight request by its exact JSON-RPC request ID, preserving its number/string type. For example, whilesession/create request 2 is still
in flight, send this notification:
Client -> server notification
id and receives no reply. When cancellation wins
before the original request commits success, the original request receives
REQUEST_CANCELLED (-32005):
Server -> client response to the original request
turn/interrupt, which targets an existing session’s turn. MCP uses the
different notifications/cancelled notification and requestId spelling
described in the MCP reference.
Callback tools
Register client-owned foreground tools withtools/register:
id, not the turn’s ID or tool_use_id.
tool_use_id is the provider’s tool-call correlation identity; name selects
the registered tool and arguments contains its JSON arguments. In the result,
content defaults to an empty string and is_error defaults to false if
omitted. A client can instead return a JSON-RPC error for the same request
ID; the server projects it as a tool-execution error.
Keep reading and answering reverse requests while waiting for the turn’s
result, or the turn cannot finish its callback. This foreground RPC exchange
is neither MCP’s pending_tool_calls/meerkat_resume continuation nor the
separate detached-job callback extensions. tool/execute is not a
client-callable catalog method.
Session methods
initialize
Handshake. Returns server capabilities.skills/list requires a
skill runtime; live/* requires a configured live transport, and
live/webrtc/answer additionally requires enabled WebRTC support. For the
full catalog rather than this runtime subset, including parameter/result type
names, use artifacts/schemas/rpc-methods.json.
string
Server name.
string
Server version.
string
Protocol contract version.
array
List of supported method names.
session/create
Create a new session and run the first turn. Setinitial_turn to "deferred" to return a pending session_id without running the first turn; that returned session_id is valid for the first turn/start, including sessions created with inline external_tools.
prompt is required. Other fields follow the per-field defaults and
override rules below; not every omitted value inherits configuration.
During execution, session/event notifications are emitted (see
Notifications).
Parameter reference
ContentInput (string | ContentBlock[])
required
The user prompt as a string or typed content-block array, for example
[{"type":"text","text":"Hello"}]. Media support depends on the selected
model/provider; see model capabilities.ContentInput[] | null
default:"null"
Host-attached injected context for the first turn. Each entry materializes
as a separate typed injected-context user-channel message immediately before
the first turn’s user message, in order; injected context is excluded from
semantic-memory indexing.
string | null
default:"null"
Non-empty, exact request-only host facts for the immediate first turn;
whitespace is significant. The pending runtime input retains the bytes for
crash retry; the Session transcript, compaction summarizer, and extraction
phase never receive them. Deferred create rejects this field; provide it on
the eventual
turn/start.string | null
default:"config/catalog default"
Model name (e.g.
"claude-opus-4-8", "gpt-5.5").string | null
default:"inferred from model"
Provider name:
"anthropic", "openai", "gemini", or "self_hosted".WireAuthBindingRef | null
default:"null"
Structural credential selector, for example
{"realm":"prod","binding":"anthropic","profile":"claude_oauth"}.
realm and binding are required when supplied; profile is optional.
Configure the referenced realm, binding, and auth profile
first. Omission/null lets the create resolver choose credentials.
A colon-delimited CLI string is not accepted; provenance is server-owned.u32 | null
default:"config default"
Max tokens per turn.
SystemPromptOverride
default:"inherit"
Omission/
null inherits the base prompt; a string sets an explicit base
prompt; {"action":"disable"} suppresses inherited/config/default/AGENTS
base prompt sources. Separately appended dispatcher, tool, and additional
instruction sections may remain, so disabling is not a promise of no System
content. This create-time policy differs from turn/start’s ordinary
ordered System-message string.object | null
default:"null"
JSON schema for structured output extraction (wrapper or raw schema).
u32 | null
default:"null (config default)"
Max retries for structured output validation. Omission/
null inherits
configuration (stock default 2); explicit 0 is preserved.HookRunOverrides | null
default:"null"
Run-scoped hook overrides (entries to add, hook IDs to disable).
bool | null
default:"null (host/factory default)"
Override built-in tools (task management, etc.). Omission/
null inherits
host/factory configuration; explicit false disables them.bool | null
default:"null (host/factory default)"
Override the shell tool (requires built-ins). Omission/
null inherits
host/factory configuration; explicit false disables it.bool | null
default:"null (host/factory default)"
Override semantic memory (
memory_search tool + compaction indexing).
Omission/null inherits host/factory configuration; explicit false
disables it.bool | null
default:"null"
Override schedule tools for this session.
null uses the surface default.bool | null
default:"null"
Override WorkGraph tools for this session.
null uses the surface default.bool
default:"false"
Keep the new session alive after its turn for comms. Omission is
false;
null is rejected on create. true requires comms_name. This differs
from turn/start’s nullable override of existing session intent.string | null
default:"null"
Agent name for inter-agent communication.
object | null
default:"null"
Provider-specific parameters (e.g., thinking config, reasoning effort).
Response fields
string
UUID of the created session.
string
The agent’s response text.
u32
Number of LLM calls made.
u32
Number of tool calls executed.
WireUsage
Token usage breakdown.
JSON (optional)
Parsed structured output when extraction succeeds. Omitted when absent.
ExtractionError (optional)
Post-turn extraction failure details:
last_output is the committed
main-turn assistant text that extraction attempted to transform, attempts
is the number of extraction attempts made, and reason describes the
failure. Omitted when absent.SchemaWarning[] (optional)
Schema compatibility warnings. Omitted when absent.
text even when post-turn
structured-output extraction fails. Inspect structured_output together with
extraction_error; absence of structured output does not prove extraction was
never requested. These optional fields in WireRunResult are omitted when
absent, not filled with null.
session/history
Read committed transcript history for an existing session.session/read, including mob-owned session IDs when mob support is enabled.
When limit is omitted, the server returns at most 100 messages. The maximum
accepted limit is 1,000 and the maximum offset is 1,000,000; larger values
fail with INVALID_PARAMS before the session store is queried.
session/export_atif
Export the currently available event audit/replay projection as an ATIF (Agent Trajectory Interchange Format) v1.7 trajectory document.agent_name, agent_version, and model_name are optional overrides for the
trajectory’s agent block; when model_name is omitted the session’s model is
used.
The export replays the currently projected event log page by page, so it
requires a host with event projection installed. Projection is asynchronous
best-effort derived state and the handler does not drain it first; an export can
therefore lag the latest committed session state. Outcomes:
- A host without durable event replay fails with
INVALID_REQUESTandevent replay is not enabled for this runtime host. - An existing session whose durable log is empty (for example a session created
with a deferred first turn) returns a trajectory that names the session with
no steps and
final_metrics.total_stepsof0. It is not a missing session. - A session that does not exist fails with
SESSION_NOT_FOUND. - A session whose durable log exceeds 500,000 events fails with
INVALID_PARAMSnaming the bound; page the log withevents/list_since, or export it to a file withrkat session export-atif, which has no such bound. - A session whose trajectory outgrows the outbound message limit (32 MiB) fails
with
BUDGET_EXHAUSTEDnaming that limit and the bytes it had accumulated. The fold measures itself page by page, so the refusal arrives while the document is still partial rather than after the whole log is in memory.
INVALID_PARAMS), the size bound is a property of the
response, so it answers the way outbound admission answers any oversized result
(BUDGET_EXHAUSTED). They cover different content mixes: an all-delta log can
hold hundreds of thousands of events and still fold into a small document, which
is what the event bound is for; a tool-heavy log outgrows the response long
before that count, which is what the size bound is for.
session/rewrite_transcript
Commit a transcript rewrite without changing the session identity. The request names a message-range selection, supplies replacement messages, and records a machine-readable reason. The session appends a rewrite commit, advances its transcript head, and retains the parent revision for audit and restore.session_id, the parent revision, the new revision, and the updated
message count. running_behavior currently supports "reject", which returns
SESSION_BUSY if the target session has active work.
session/transcript_revision
Read one retained transcript revision body by revision id."current" to read the active transcript head, or pass a concrete revision returned
by session/rewrite_transcript or session/restore_transcript_revision.
session/transcript_revisions
List retained transcript revision commits (oldest first) together with the current transcript head revision. Each entry records the revision the commit produced, the parent it was applied against, the recorded actor, the rendered rewrite reason, and the commit timestamp.head_revision still names
the active transcript head. Pass a returned revision to session/transcript_revision
to read the retained body, or to session/restore_transcript_revision to restore it.
session/restore_transcript_revision
Restore a retained transcript revision as the active transcript head without changing the session identity. Restore is represented as another rewrite commit so the graph remains append-only even though the source transcript projection is updated in place.revision accepts the same selector as session/transcript_revision: "current"
resolves to the head revision, so restoring it surfaces the typed no-op rewrite error.
session/fork_at
Create a new idle session whose transcript is the source session prefix ending beforemessage_index. The source session is not mutated.
running_behavior currently supports "reject", which returns SESSION_BUSY if the source session has active work.
session/fork_replace
Create a new idle session from the source prefix throughmessage_index, replacing the addressed message or block with a typed replacement. Later source messages are intentionally omitted so callers continue from the edited branch instead of replaying stale descendants.
message, user_content_block, assistant_block, and tool_result_content_block. Edits always create a new session identity.
session/list
List sessions.WireSessionSummary with created_at and updated_at in
Unix seconds. is_active is the reported session activity flag, not a
runtime-state enum. Optional session_ref and non-empty labels may also be
present.
session/list is owner-paginated rather than load-all/truncate: omitted
limit defaults to 100, the maximum is 1,000, and offset is capped at
1,000,000. The same 100/1,000 collection limits apply to
events/list_since, workgraph/list, workgraph/ready,
workgraph/snapshot, and workgraph/events.
WorkGraph ready-set and snapshot projections are atomic only within explicit
process bounds. A projection that would need to scan more than 1,000 items,
1,000 edges, or 1,000 attention bindings fails closed with
INVALID_PARAMS; narrow the realm/namespace/filter and retry. The store does
not materialize an oversized graph and truncate it after the fact.
session/read
Get session metadata.string
required
Session ID to read.
WireSessionInfo, not the list summary: it includes
model/provider but no total_tokens or state. Timestamps are Unix
seconds, and is_active is the reported activity flag. Optional fields
include session_ref, last_assistant_text, resolved_capabilities, and
non-empty labels.
session/archive
Remove a session from the runtime.string
required
Session ID to archive.
Turn methods
turn/start
Start a new turn on an existing session.session/create. Fails with error code -32002 (SESSION_BUSY) if a turn is already in progress.
string
required
Session ID to continue.
ContentInput (string | ContentBlock[])
required
The follow-up prompt as text or typed content blocks, for example
[{"type":"text","text":"Continue"}]. Media support is model/provider
dependent; see model capabilities.ContentInput[] | null
default:"null"
Host-attached injected context for this turn. Each entry materializes as a
separate typed injected-context user-channel message immediately before the
turn’s user message, in order; injected context is excluded from
semantic-memory indexing.
string | null
default:"null"
Non-empty, exact request-only host facts for this newly admitted turn;
whitespace is significant. Projected before the admitted conversational
user message for foreground model calls only; never appended to Session.
string | null
default:"from session"
Model override for this turn. On pending (deferred) sessions this sets the model before materialization. On materialized sessions this hot-swaps the LLM client for the remainder of the session.
string | null
default:"from session"
Provider override (e.g.
"anthropic", "openai", "gemini"). Typically inferred from model.TurnMetadataOverride | null
default:"preserve"
Omission/
null preserves current session parameters.
{"action":"set","value":{"thinking_budget_tokens":10000}} replaces the
stored override; {"action":"clear"} clears it. The compatibility form
{"thinking_budget_tokens":10000} is also accepted as a Set payload.
The inner object is the typed ProviderParamsOverride with
temperature, top_p, max_output_tokens, reasoning,
thinking_budget_tokens, and typed provider_tag extensions, not an
arbitrary bag of provider-native keys. Applies alongside model/provider
overrides.TurnMetadataOverride | null
default:"preserve"
Omission/
null preserves the session’s binding.
{"action":"set","value":{"realm":"prod","binding":"anthropic","profile":"claude_oauth"}}
selects an existing configured binding/profile;
profile is optional. A bare binding object such as
{"realm":"prod","binding":"anthropic"} is also accepted as a Set payload.
{"action":"clear"} removes the persisted override and resumes normal
credential resolution; it does not disable credentials. Do not supply
server-owned provenance.u32 | null
default:"from session"
Max tokens override.
object[] | null
Tagged structured skill references to resolve and inject for this turn. Each entry uses
{"kind":"structured","source_uuid":"...","skill_name":"..."}.turn/interrupt
Cancel an in-flight turn. No-op if the session is idle.string
required
Session ID to interrupt.
interrupted is derived from the typed result. The public classifier
reports "interrupted"/true for an existing idle session’s no-op as well
as a live interruption; it is not proof that a live turn was cancelled.
An admitted deferred/staged session has no materialized run to interrupt and
returns "staged_noop"/false. Failures use JSON-RPC errors rather than
these successful outcomes.
Event methods
session/external_event
Queue a runtime-backed external event for an existing session.session/peer_response_terminal
Admit a correlated terminal peer response through the typed runtime ingress.string
required
Session ID to admit the peer response to.
string
required
Canonical peer routing ID.
string
Optional presentation label. It is not used as routing identity.
string
required
Peer correlation ID for the request this terminal response completes.
string
required
Terminal response status:
"completed", "failed", or "cancelled".any JSON
required
Peer-returned terminal payload.
-32603 if runtime admission fails, -32602 if the session locator is invalid.
This is a queue-only runtime admission path; it does not create a second direct execution loop.
comms/peers
List discoverable peers from configuredTrustedPeers and active in-process registrations. Requires the comms feature.
string
required
Session ID to query peers for.
peer_id as the to value for comms/send; name is display-only and
may collide. Each entry uses the canonical typed PeerDirectoryEntry shape:
address is {transport, endpoint}, source records discovery provenance,
sendable_kinds states supported comms operations, capabilities carries the
versioned capability envelope, and meta contains supplementary description
and labels.
skills/list
List all skills with provenance information, including active and shadowed entries.array
List of
SkillEntry objects with key, name, description, scope, canonical source provenance, is_active, and optional canonical shadowed_by provenance.-32603 if skills are not enabled.
skills/inspect is not part of the advertised RPC catalog. Use skills/list for provenance, or the CLI/MCP skill inspection surfaces when full skill bodies are needed.
Event replay and durable projection
Theevents/* family is the complete cursor-based replay surface for hosts that
report runtime/capabilities.features.event_replay = true:
events/latest_cursorreturns the latest cursor for a typed scopeevents/list_sincepages envelopes after an optional cursor and reportslatest_cursorplushas_moreevents/snapshotreturns a point-in-time session snapshot paired with its cursor
EventReplayScope is
{"type":"session","session_id":"..."}. A cursor is scope-bound and
monotonic; scope mismatch and a cursor ahead of the latest sequence fail typed
rather than being silently clamped.
Live session subscribers use a bounded 256-entry broadcast. If a consumer
falls behind, it receives AgentEvent::StreamTruncated with
StreamLagged { dropped }, then retained events. Persistent hosts can install
one internal audit projector through a separate unbounded queue and warn when
the queue reaches 1,024 pending events. UI lag cannot drop projector input.
EventStore append remains asynchronous best-effort derived state: an append
fault latches projection halt and replay fails closed, but does not undo the
committed turn. The RuntimeStore/backend carrier remains session authority;
SessionStore rows and EventStore are component/content and projection seams.
Durable jobs and monitors
The app-facingjobs/* methods expose realm-scoped job projections without
worker attempt IDs or fence tokens. Read methods cover summaries, progress,
terminal results, artifact references, and health. jobs/cancel commits a
cancellation request, jobs/retry asks the generated job machine to schedule a
retry at retry_due_at_ms, and subscribe/unsubscribe manage durable
record/notification/event delivery.
monitors/start is the explicit high-trust script-monitor entry point. It
requires session_id, a caller-stable submission_key, command, positive
timeout_secs, restart_class, and delivery. The default output protocol is
framed_jsonl; lines is also supported. Agent-authored monitors cannot claim
the adoptable restart class. Detached shell execution fails closed unless the
realm supplies persistent job and blob stores plus its runtime delivery
projector; there is no volatile fallback.
The mobkit/jobs/* methods are host-worker mutation methods. Every heartbeat,
progress, checkpoint, completion, failure, or cancellation acknowledgement is
admitted under the exact attempt and fence authority carried by its typed
params. Application clients should not synthesize worker mutations from the
safe JobSummary projection.
jobs/health
jobs/health returns { "detached_jobs": JobHealthSummary }. The summary
separates three states:
ok: the complete census observed no unhealthy job factdegraded: the census observed a stale lease, attention state, or backlogunreadable: a read failed or the bounded census was truncated, so it did not establish health
coverage is either {"kind":"complete"} or
{"kind":"truncated","scanned":N,"limit":N}. A truncated census always
has status: "unreadable"; its counts are lower bounds. Keep
pending_outbox_jobs separate from runtime_inbox_backlog: the former counts
realm jobs whose delivery was never handed to a runtime, while the latter is a
host-store-scoped count of committed runtime deliveries that were accepted but
not drained. It can therefore include sessions from other logical realm IDs on
the same host.
Blobs and artifacts
blob/get accepts a realm-local blob ID and returns BlobPayload with
blob_id, media_type, and base64 data. Assistant image blocks and job
results can carry blob references instead of inline bytes.
Artifacts are stable metadata records over generated output. artifact/list
filters by optional session_id and exact labels, artifact/get reads one
record, and artifact/download resolves a blob-backed payload with an optional
expected_media_type integrity check. Artifact IDs are opaque realm-local
identities, not filesystem paths.
Approvals
The approval family consists ofapproval/request, approval/list,
approval/get, and approval/decide. Requests carry a typed owner, resource,
proposed action, risk, non-empty allowed_decisions, optional expiry,
metadata, and provenance.
Request
runtime, session, mob, run, tool_call, or
external_member. Resource kinds are file, shell_command, tool_call,
device, runtime, network, or other; risk is low, medium, high, or
critical. Proposed action kinds are shell_command, file_write,
file_delete, network_call, device_control, tool_call, or other.
Decisions are approve or deny, and record status is pending,
approved, denied, expired, or cancelled. The service rejects an empty
decision set, an unsupported decision, or a second terminal decision. Expiry
is evaluated lazily on approval/get, approval/list, or approval/decide:
a past-due request may first be stored as pending, but its next observation
persists expired and it can no longer be decided. cancelled is a durable
status that can be restored, but the current public RPC service has no
approval/cancel transition.
external_member is an audit owner shape, not a cross-host forwarding
contract. Multi-host v1 keeps approval records and decisions host-local.
RPC opens <store_path>/approvals.json whenever its PersistenceBundle
exposes a store path, including a named built-in memory realm. Only an ad hoc
bundle with no store path falls back to process-local approval records. The
approvals runtime host feature is true only when this approval service is
persistent.
The file store is a one-host sidecar. It serializes updates with a
process-local mutex and atomic temp-file rename, but has no cross-process lock,
revision, or CAS. Run only one approval-owning RPC host against a given
approvals.json; shared-state multi-host approval writing is not supported.
These methods maintain approval audit lifecycle records only. Creating or
deciding a record does not automatically gate, authorize, or execute a shell
command, tool call, device action, or any other side effect.
Schedules, WorkGraph, and multi-host mobs
The schedule family provides CRUD, pause/resume, occurrence listing, tool schema discovery, and direct typed tool calls. Schedule and WorkGraph state are realm-scoped durable domains when the owning feature and store are available. Public WorkGraph RPC is intentionally observational: item get/list, ready work, snapshot, event history, goal status, and attention-list reads. Agents mutate WorkGraph through theworkgraph_* tool family; trusted in-process hosts own
goal and attention mutation authority.
Multi-host mob RPC includes host bind/revoke, host and route observation,
scope grants, hard cancel, placed member history, and member live-channel
control. Check runtime/capabilities.features.multi_host_mobs rather than
inferring support from generic mobs or the server version.
mob/member_live_open supports WebSocket transport only for both local and
placed members. A request for transport: "webrtc" is rejected; WebRTC uses
the session-scoped live/open and live/webrtc/answer flow below only when
the caller can address the owning session directly. In multi-host v1,
controller session/* methods do not proxy a placed member’s host-local
session, so there is no controller-side WebRTC path to a placed member.
Durable member role migration is not part of mob/spawn, profiles, or any
public JSON-RPC/SDK params. The one-shot resume_from_role declaration exists
only on trusted in-process SpawnMemberSpec and the private remote-host
MaterializeLaunchMode::Resume wire. It authorizes one exact cold migration
from the stored predecessor role while keeping mob ID, member identity,
session, and transcript fixed; live sessions and mismatched declarations fail
closed.
workgraph/get
Read one WorkGraph item by id. Requires theworkgraph feature and a
realm-scoped runtime; a runtime without a realm identity returns a typed fault
instead of inventing a default scope.
WorkItem shape is in the
WorkGraph reference.
string
required
The
WorkItemId.string
Defaults to the runtime’s realm. Any other value is rejected: the service holds exactly one
WorkGraphNamespaceGrant.string
Defaults to the granted namespace (
default on standard surfaces). Any other value is rejected.WorkEvidenceRef appears (the
workgraph_add_evidence tool argument, host mutation surfaces such as MobKit’s
mobkit/workgraph/evidence/add), has the shape
{"kind": string, "id": string, "label"?: string, "summary"?: string}. kind
is an opaque provenance label (artifact, pr, log, summary,
notification, …); the reserved confirmation kinds (host_confirmation,
principal_confirmation, supervisor_confirmation, reviewer_confirmation)
are refused from generic callers and stamped only by trusted goal-confirm
authority.
workgraph/list
List items in the granted realm and namespace. Params are aWorkItemFilter;
all fields are optional and an empty object lists live work.
string
Defaults to the runtime’s realm; must equal the granted realm.
string
Defaults to the granted namespace; must equal it.
boolean
default:"false"
Present in the wire shape, rejected by the standard single-grant services. A namespace grant never implies a multi-namespace capability.
WorkStatus[]
Keep only items whose status is listed:
open, in_progress, blocked, completed, cancelled, failed. Checked before the terminality rule below.string[]
Every listed label must be present on the item (a conjunction).
["release", "docs"] returns only items carrying both; filter on one label to widen the result.boolean
default:"false"
When false,
completed, cancelled, and failed items are dropped even if statuses names them: {"statuses": ["completed"]} alone returns an empty list. Set it to true to read closed work.integer
default:"100"
Maximum items returned. Omitted means 100; values above 1,000 fail with
INVALID_PARAMS before the store is queried.cancelled (it then hides behind
include_terminal); to replace it, create the successor and link a
supersedes edge from the new item (from_id) to the old one (to_id).
workgraph/ready
List items whose machine-derived readiness holds right now: live status, no unresolvedblocks edge, parent join satisfied, and time gates passed. Params
are a ReadyWorkFilter.
string
Defaults to the runtime’s realm; must equal the granted realm.
string
Defaults to the granted namespace; must equal it.
string[]
Every listed label must be present (same conjunction as
workgraph/list).integer
default:"100"
Maximum items returned after sorting by priority (high first), then creation time, then id. Omitted means 100; above 1,000 is
INVALID_PARAMS.include_terminal here because a terminal item is never ready.
Readiness is evaluated atomically over the namespace; a namespace with more
than 1,000 items fails with INVALID_PARAMS and must be narrowed rather than
truncated. The result is {"items": [...]}.
workgraph/snapshot
Read one atomic observation of the namespace. Params are aWorkGraphSnapshotFilter with the same fields and semantics as
WorkItemFilter (realm_id, namespace, all_namespaces, statuses,
labels, include_terminal, limit).
statuses, labels, and include_terminal select the items exactly as in
workgraph/list; limit then bounds them (default 100, maximum 1,000).
edges contains only edges whose both endpoints are among the returned items,
attention only bindings on those items, and ready_item_ids the subset of
returned items that is ready at captured_at. A snapshot therefore omits an
edge to a terminal item unless include_terminal is true. The atomic scan is
bounded before filtering: a namespace with more than 1,000 items, 1,000 edges,
or 1,000 attention bindings fails with INVALID_PARAMS and must be narrowed.
workgraph/events
Read the public event history of the namespace. Params are aWorkGraphEventFilter.
string
Defaults to the runtime’s realm; must equal the granted realm.
string
Defaults to the granted namespace; must equal it.
boolean
default:"false"
Rejected by the standard single-grant services.
integer
Return only events with
seq strictly greater than this value. Use the last seq you saw, or a snapshot’s event_high_water_mark, to page forward.integer
default:"100"
Maximum events returned; above 1,000 is
INVALID_PARAMS.seq, realm_id, namespace, optional item_id, kind
(created, updated, readiness_observed, claimed, released, blocked,
closed, linked, evidence_added, attention_created,
attention_updated), at, a kind-specific payload, and any typed facts
committed with the mutation ({"kind": "item_ready" | "lease_expired" | "namespace_terminal", ...}). Flow execution-binding events are kept in the
durable journal but omitted from this projection.
workgraph/goal/status
Read one goal: the work item plus its attention binding. Params are aGoalStatusRequest.
string
required
The
WorkAttentionBindingId.string
Defaults to the runtime’s realm; must equal the granted realm.
string
Defaults to the granted namespace; must equal it.
{"item": WorkItem, "attention": WorkAttentionBinding}. An
unknown binding is INVALID_PARAMS with the typed not-found message.
workgraph/attention/list
List attention bindings in the granted namespace, optionally narrowed by target or status. Params are anAttentionListRequest; an empty object lists
every binding.
string
Defaults to the runtime’s realm; must equal the granted realm.
string
Defaults to the granted namespace; must equal it.
WorkAttentionTarget
{"kind": "session", "session_id": ...} or {"kind": "lowered_owner", "owner_key": {...}}.WorkAttentionStatus
Tagged by
state: {"state": "active"}, {"state": "paused", "until"?: timestamp}, {"state": "superseded"}, or {"state": "stopped"}.{"attention": [WorkAttentionBinding, ...]}. Attention
reassignment and policy escalation are not RPC methods; they are
authority-bearing tools injected into turns that carry an active attention
projection.
Live channel methods
Live channels provide low-latency audio and text streaming plus model-gated still-image input on sessions that use a realtime-capable model. Create a session with a model likegpt-realtime-2, then call live/open to start the channel explicitly. Start rkat-rpc with --live-ws <addr> for WebSocket transport, or with the feature-gated --live-webrtc flag for WebRTC signaling. At least one live transport must be enabled for the live/* family to be advertised. See the Live Channels guide for the full flow.
Opening a live channel
Create a session on a realtime-capable model and calllive/open:
live/open returns a LiveOpenResult with transport bootstrap (e.g. WebSocket URL), WireLiveChannelCapabilities, and WireLiveContinuityMode.
string
required
Session whose canonical history seeds the live channel.
provider_managed | explicit_commit | null
default:"null"
Optional input-turning mode. Omitted preserves provider-managed behavior.
websocket | webrtc | null
default:"null"
Optional live transport. Omitted uses the server default.
usize | null
default:"null"
Optional positive serialized-character budget for the core-owned seed
message window. Omitted preserves the complete canonical history. When
present, core selects a recent complete-turn replay suffix. Contiguous
System, SystemNotice, and injected-context messages immediately preceding
a selected user turn stay with that turn; older System rows may be omitted.
An existing compaction-summary head may also be retained. Any truncation
returns degraded continuity. Zero is rejected.
live/webrtc/answer
For WebRTC, request"transport": "webrtc" on live/open. The returned
transport bootstrap contains a single-use token and names
live/webrtc/answer as the canonical signaling method. Send the browser offer
SDP with that token:
Request
{ "answer_sdp": "v=0..." }. Tokens are channel-bound,
short-lived, and single-use; replay, expiry, or a channel mismatch fails typed.
live/status
Read the current state of a live channel.live/send_input
Send one typed input chunk. Image input is available only whenLiveOpenResult.capabilities.image_in is true; an image is staged as context
for the next text, audio, or explicitly committed response. Every image must
carry a caller-stable, session-scoped idempotency_key.
The JSON-RPC JSONL transport accepts frames up to 64 MiB, excluding the
trailing newline. Direct live WebSocket frames are a different input path:
they support JSON text chunks and negotiated raw PCM audio only, with a 2 MiB
aggregate/per-frame ceiling. Images always use this JSON-RPC method, even when
the channel’s observation/audio transport is WebSocket or WebRTC.
Server-originated JSON-RPC responses are capped at 32 MiB and queued
notifications at 8 MiB. Their byte/count reservations are process-wide and
remain owned through the bounded transport write; oversized collection reads
must use pagination instead of relying on socket buffering.
image_input_idempotency_conflict. If text or audio is already staged, commit
it before submitting an image; otherwise the image is rejected as
image_input_requires_commit.
{ "status": "sent" } proves only that the adapter queue accepted the
command. Wire validation and queue admission can instead fail immediately with
typed LiveSendInputErrorData. A check that fails after acceptance is emitted
as a typed command_rejected observation. Only a
user_content_committed observation carrying the same idempotency_key
proves the provider-acknowledged image reached durable canonical session
history. The receipt is redacted: it contains identity, ordering, media type,
and the key, never image bytes.
Each image is limited to 20 MiB decoded, and canonical live image history is
limited to 40 MiB decoded in aggregate, counting repeated references as
separate occurrences. live/send_input rejects a new image that would cross
the aggregate ceiling as image_input_history_budget_exceeded before provider
send or persistence. That keeps every successfully committed live image
reopenable. Legacy or out-of-band history already above the ceiling, missing
blobs, and content-address mismatches fail live/open rather than trimming,
substituting, or changing image context. See the
Live Channels guide for retry and
transport details.
Live channel lifecycle methods (live/*)
WebSocket calls require
--live-ws <addr>. WebRTC open/answer requires a build
with the live-webrtc feature and startup with --live-webrtc. If both are
enabled, select the transport explicitly or accept the server default.
MCP methods
mcp/add
Stage a live MCP server addition for a running session. The server is connected at the next turn boundary.string
required
Session ID to add the server to.
object
required
Typed MCP server configuration. Include
name with either stdio fields (command, optional args/env) or HTTP fields (url, optional headers/transport).bool
default:"false"
Whether to also write the server to disk config.
mcp/remove
Stage removal of an MCP server from a running session. Active tool calls drain before the server disconnects at the next turn boundary.string
required
Session ID to remove the server from.
string
required
Name of the MCP server to remove.
bool
default:"false"
Whether to also remove from disk config.
mcp/reload
Reload one or all MCP servers for a running session. Useful after config changes.string
required
Session ID to reload servers for.
string | null
default:"null"
Specific server to reload. If null, reloads all servers.
Models
models/catalog
Return the curated model catalog with provider profiles, capability metadata, and parameter schemas.INVALID_PARAMS.
ContractVersion (object)
Structured catalog contract version with integer
major, minor, and
patch components. Unlike initialize, this result does not stringify it.array
List of provider entries, each containing
provider, default_model_id, and models.object
Model profile with capability flags (
supports_temperature, supports_thinking, supports_reasoning) and params_schema.Capabilities
capabilities/get
Return capabilities registered by the running build’s linked owners, with status resolved against current config. This is not an enumeration of every capability vocabulary member: an unavailable feature can have no row at all. A registered declaration can still report{"NotCompiled":{"feature":"..."}}; missing declarations are not automatically
synthesized with that status.
status values:
Config methods
config/get
Read current realm config envelope.Config write results
RPCconfig/set and config/patch return ConfigWriteResult: the committed
ConfigEnvelope fields (config, generation, realm_id, instance_id,
backend, resolved_paths) are flattened at the top level, with optional
live_propagation.
The report is omitted when the write does not fan out; the current fan-out
trigger is a change to agent.model. When present, it contains:
Inspect
skipped even when clean is true: clean does not mean every
session changed, nor that identity lookup succeeded for every session.
It describes this report’s synchronous failure lists, not eventual provider
application. Observe subsequent live-channel status and errors to determine
whether an accepted refresh was applied.
Propagation occurs after the config generation commits. A non-clean
report is a successful config write with live-propagation failures, not a
rollback of the committed generation. Reconcile the listed sessions/channels
separately. This report is specific to RPC writes, not config/get or the
REST/MCP config envelope.
config/set
Replace config, optionally using generation CAS. Returns the config write result.config/patch
Merge-patch the config (RFC 7396). Returns the same config write result. This example changes onlymax_tokens_per_turn, leaving agent.model unchanged, so its result omits
live_propagation.
config/set also accepts a direct config object as params for compatibility. In that mode, no CAS check is applied.Notifications
During turn execution, the server emitssession/event notifications with an
EventEnvelope<AgentEvent> under params.event:
params.event.payload.type and (for text deltas)
params.event.payload.delta, not params.event.type. event_id, typed
source, seq, and timestamp_ms belong to the envelope; optional mob_id
is envelope metadata.
Event types match the AgentEvent enum in crates/meerkat-core/src/event.rs
(agent_event_type assigns each variant its stable wire name) and its schema
projection in artifacts/schemas/events.json. New variants must appear in
both that enum and this table. The decodable vocabulary includes the
explicitly marked legacy row; not every variant is emitted by current writers.
stream_truncated is subscriber-local. It reports UI-ring lag, not an
EventStore projection result. Use events/list_since or events/snapshot
when that optional replay surface is healthy, and reconcile against current
session state after receiving it.
session/stream_event notifications
When a session event stream is open (viasession/stream_open), the server
emits session/stream_event notifications. params.event is the same nested
EventEnvelope<AgentEvent> as in session/event, scoped by an outer stream
handle.
sequence numbers the explicit stream’s deliveries and is distinct
from the source envelope’s event.seq. Outer stream_id and session_id
are routing fields; scoped streams can additionally carry scope_id and
scope_path. None of these flattens or replaces event.payload.
session/stream_end notifications
An explicit session stream terminates with this notification:outcome is "remote_end", "terminal_error", or "explicit_close".
Calling session/stream_close terminates the handle with explicit_close;
normal upstream exhaustion uses remote_end. When a terminal error is
reported, the notification additionally contains
error: {"code":"stream_queue_overflow","message":"..."} or
error: {"code":"stream_receiver_gone","message":"..."}.
stream_truncated is an event-level lag observation, not this terminal
protocol. Stop consuming a terminated handle and reopen/reconcile as needed.
A disconnected transport or lost notification receiver cannot be relied on to
deliver its final end notification.
mob/stream_event notifications
When a mob event stream is open (viamob/stream_open), the server emits mob/stream_event notifications. Requires the mob feature.
AttributedEvent (source member identity + profile + envelope). For per-member streams the event is the raw EventEnvelope<AgentEvent>. Runtime incarnation ids and fence tokens are bridge-internal and are not part of public stream payloads.
mob/stream_end notifications
Requires themob feature. The mob terminal payload omits session_id:
outcome is "remote_end", "terminal_error", or
"explicit_close"; mob/stream_close selects explicit-close termination.
Conditional error contains code ("stream_queue_overflow" or
"stream_receiver_gone") and message; it is absent without an error.
Stop using the terminated handle and reopen/reconcile as appropriate.
Transport disconnection can prevent delivery of this final notification.
Neither stream-end notification is a callable RPC method.
Error codes
Standard JSON-RPC codes plus Meerkat-specific application codes:Architecture
The RPC server is stateful: agents stay alive between turns through the shared runtime-backed service also used by REST and MCP. RPC’s distinguishing surface is its broad JSON-RPC method catalog, scoped subscriptions, and server notifications.Agent. This solves the cancel(&mut self) requirement without mutex. Commands (StartTurn, Interrupt, Shutdown) are sent via channels.
The JSONL writer and notification queues are bounded, and ordinary session
subscriptions are best-effort. A slow client can therefore receive a typed
stream_truncated marker instead of every live event. Persistent event audit
projection can use a separate unbounded internal queue, so UI lag does not drop
projector input. The EventStore remains asynchronous derived state rather than
session completeness authority.