Skip to main content
Public SDK interfaces use camelCase by default. Protocol-bound multimodal content keys (media_type, blob_id, duration_ms) intentionally remain wire-shaped.

Imports

Everything public is re-exported from the package root:
import {
  MeerkatClient,
  Session,
  LiveChannel,
  Mob,
  EventStream,
  EventSubscription,
  MeerkatError,
  CapabilityUnavailableError,
  SessionNotFoundError,
  SkillNotFoundError,
  CONTRACT_VERSION,
} from "@rkat/sdk";

import type {
  RunResult,
  Usage,
  SessionInfo,
  SessionOptions,
  LiveChannelOptions,
  LiveOpenResult,
  LiveStatusResult,
  LiveInputChunkWire,
  ContentBlock,
  Capability,
  SkillKey,
  SkillRef,
  SkillRuntimeDiagnostics,
  SchemaWarning,
  AgentEvent,
  AgentEventEnvelope,
  AttributedMobEvent,
  EventEnvelope,
  AttributedEvent,
  MobMember,
  MobStatus,
  MobSummary,
  SpawnSpec,
  MobFlowStatus,
  MobLifecycleAction,
  MobDefinition,
  TextDeltaEvent,
  TurnCompletedEvent,
  // ... all event types
} from "@rkat/sdk";

Additional client wrappers

The package exports more than the core session lifecycle. The MeerkatClient also exposes:
  • auth-profile helpers: authProfileList, authProfileGet, authProfileCreate, authProfileDelete
  • auth-login helpers: authLoginStart, authLoginComplete, authLoginDeviceStart, authLoginDeviceComplete, authLoginProvisionApiKey, authStatusGet, authLogout
  • realm helpers: realmList, realmGet
  • typed ingress helper: sendPeerResponseTerminal
  • MCP live-op helpers: mcpAdd, mcpRemove, mcpReload
  • blob/skill helpers: getBlob, listSkills
  • live-channel helpers: liveOpen, liveStatus, liveClose, liveSendInput, liveSendInputImage, liveSendInputVideoFrame, liveCommitInput, liveInterrupt, liveTruncate, liveRefresh, and parseLiveObservation

Core types

RunResult

The result returned by session.turn(), session.invokeSkill(), and deferred.startTurn(). Session creation returns runtime-backed Session or DeferredSession wrappers, and the latest RunResult remains available through session.lastResult or stream.result after an EventStream is fully consumed.
interface RunResult {
  readonly sessionId: string;
  readonly sessionRef?: string;
  readonly text: string;
  readonly turns: number;
  readonly toolCalls: number;
  readonly usage: Usage;
  readonly terminalCauseKind?: TurnTerminalCauseKind;
  readonly structuredOutput?: unknown;
  readonly extractionError?: ExtractionError;
  readonly schemaWarnings?: readonly SchemaWarning[];
  readonly skillDiagnostics?: SkillRuntimeDiagnostics;
}
FieldTypeDescription
sessionIdstringStable UUID for this session
sessionRefstring | undefinedOptional human-readable reference
textstringFull assistant text from the last LLM turn
turnsnumberNumber of LLM turns in this run
toolCallsnumberTotal tool calls executed in this run
usageUsageToken usage for this run
terminalCauseKindTurnTerminalCauseKind | undefinedMachine-owned terminal cause when success carries a typed terminal condition (e.g. budget exhaustion)
structuredOutputunknown | undefinedParsed structured output when outputSchema was provided
extractionErrorExtractionError | undefinedExtraction failure details (lastOutput, attempts, reason) when schema extraction failed after a completed main run
schemaWarningsSchemaWarning[] | undefinedWarnings emitted when structured output didn’t fully match the schema
skillDiagnosticsSkillRuntimeDiagnostics | undefinedRuntime diagnostics from the skill subsystem

Session

client.createSession() returns a runtime-backed Session wrapper.
class Session {
  readonly id: string;
  readonly ref?: string;
  readonly text: string;
  readonly usage: Usage;
  readonly turns: number;
  readonly toolCalls: number;
  readonly structuredOutput?: unknown;
  readonly lastResult: RunResult;
}
The Session methods are thin conveniences over canonical runtime calls:
  • await session.turn(...)
  • session.stream(...)
  • await session.history(...)
  • await session.archive()
  • await session.interrupt()
  • await session.invokeSkill(...)
  • await session.subscribeEvents()

DeferredSession

client.createDeferredSession() reserves session identity now and runs the first turn later through await deferred.startTurn(...).

LiveChannel

LiveChannel is the session-bound helper for the live/* RPC surface. RealtimeChannel was removed with the live-adapter surface; use LiveChannel.session(client, sessionId, options?) instead.
const channel = LiveChannel.session(client, session.id, {
  turningMode: "explicit_commit",
});

const opened: LiveOpenResult = await channel.open();
await channel.sendInputText("hello");
await channel.commitInput("text");
const status: LiveStatusResult = await channel.status();
const refresh = await channel.refresh();
await channel.close();
The helper stores the channel_id returned by live/open. It does not own the WebSocket transport; connect to opened.transport.url with the returned token using the transport appropriate for your app.

Usage

Token usage for a single run. All fields are camelCase — there is no total_tokens field.
interface Usage {
  readonly inputTokens: number;
  readonly outputTokens: number;
  readonly cacheCreationTokens?: number;
  readonly cacheReadTokens?: number;
}

SessionInfo

Summary returned by client.listSessions().
interface SessionInfo {
  readonly sessionId: string;
  readonly sessionRef?: string;
  readonly createdAt: number;
  readonly updatedAt: number;
  readonly messageCount: number;
  readonly totalTokens?: number;
  readonly isActive: boolean;
  readonly model?: string;
  readonly provider?: string;
  readonly lastAssistantText?: string;
  readonly resolvedCapabilities?: ResolvedModelCapabilities;
  readonly labels: Readonly<Record<string, string>>;
}

Capability

A single runtime capability entry, as returned by client.capabilities.
interface Capability {
  readonly id: string;
  readonly description: string;
  readonly status: string;  // "Available", "DisabledByPolicy", "NotCompiled", etc.
}
Status values may be emitted as externally-tagged Rust enum objects (e.g. { DisabledByPolicy: { ... } }). The SDK normalizes these to the key string automatically.

ContentBlock

Content blocks are used in multimodal prompts and tool results. Both createSession() and session.turn() accept string | ContentBlock[] as the prompt parameter.
type ContentBlock =
  | { type: "text"; text: string }
  | { type: "image"; media_type: string; data: string; source?: "inline" }
  | { type: "image"; media_type: string; source: "blob"; blob_id: string }
  | { type: "video"; media_type: string; duration_ms: number; data: string; source?: "inline" }
  | { type: "video"; media_type: string; duration_ms: number; source: "uri"; uri: string };
VariantFieldsDescription
texttext: stringPlain text content
imagemedia_type: string, data: stringBase64-encoded image with MIME type (e.g. "image/png")
// Multimodal prompt example
const session = await client.createSession([
  { type: "text", text: "Describe this image" },
  { type: "image", media_type: "image/png", data: base64Data },
]);

SchemaWarning

Emitted when a structured output response did not fully conform to the requested schema.
interface SchemaWarning {
  readonly provider: string;
  readonly path: string;
  readonly message: string;
}

Skill types

SkillKey

Structured skill identifier.
interface SkillKey {
  readonly sourceUuid: string;
  readonly skillName: string;
}

SkillRef

A skill reference is a structured SkillKey.
type SkillRef = SkillKey;

SkillRuntimeDiagnostics

Runtime diagnostics from the skill subsystem. Present on RunResult.skillDiagnostics when the server emits skill health data.
interface SkillRuntimeDiagnostics {
  readonly sourceHealth: SourceHealthSnapshot;
  readonly quarantined: readonly SkillQuarantineDiagnostic[];
}

interface SourceHealthSnapshot {
  readonly state: string;
  readonly invalidRatio: number;
  readonly invalidCount: number;
  readonly totalCount: number;
  readonly failureStreak: number;
  readonly handshakeFailed: boolean;
}

interface SkillQuarantineDiagnostic {
  readonly sourceUuid: string;
  readonly skillId: string;
  readonly location: string;
  readonly errorCode: string;
  readonly errorClass: string;
  readonly message: string;
  readonly firstSeenUnixSecs: number;
  readonly lastSeenUnixSecs: number;
}

EventStream

EventStream is an AsyncIterable<AgentEvent> returned by createSessionStreaming() and session.stream(). It yields typed events as the agent runs, then makes the final RunResult available on stream.result.
class EventStream implements AsyncIterable<AgentEvent> {
  readonly sessionId: string;
  readonly result: RunResult;  // throws if accessed before iteration completes

  [Symbol.asyncIterator](): AsyncGenerator<AgentEvent>;

  /** Consume all events and return the final RunResult. */
  collect(): Promise<RunResult>;

  /** Consume events, accumulate text deltas, return [fullText, result]. */
  collectText(): Promise<[string, RunResult]>;
}

Iterating

for await (const event of session.stream("Summarise this")) {
  if (event.type === "text_delta") {
    process.stdout.write(event.delta);
  }
}
const result = stream.result;

collect()

Discards all events and returns the final result:
const result = await stream.collect();

collectText()

Accumulates all text_delta events and returns the joined string alongside the result:
const [fullText, result] = await stream.collectText();
console.log(fullText);
console.log("Output tokens:", result.usage.outputTokens);

Typed events

All events are discriminated on the type field (snake_case, matching the wire protocol). All other fields are camelCase.

AgentEvent union

type AgentEvent =
  | RunStartedEvent
  | RunCompletedEvent
  | ExtractionSucceededEvent
  | ExtractionFailedEvent
  | RunFailedEvent
  | TurnStartedEvent
  | TextDeltaEvent
  | TextCompleteEvent
  | ToolCallRequestedEvent
  | ToolResultReceivedEvent
  | TurnCompletedEvent
  | ToolExecutionStartedEvent
  | ToolExecutionCompletedEvent
  | ToolExecutionTimedOutEvent
  | CompactionStartedEvent
  | CompactionCompletedEvent
  | CompactionFailedEvent
  | BudgetWarningEvent
  | RetryingEvent
  | HookStartedEvent
  | HookCompletedEvent
  | HookFailedEvent
  | HookDeniedEvent
  | SkillsResolvedEvent
  | SkillResolutionFailedEvent
  | InteractionCompleteEvent
  | InteractionFailedEvent
  | StreamTruncatedEvent
  | ToolConfigChangedEvent
  | BackgroundJobCompletedEvent
  | TranscriptRewriteCommittedEvent
  | MalformedEvent
  | UnknownEvent;
Event parsing fails closed: a type outside the generated KNOWN_AGENT_EVENT_TYPES inventory throws a MeerkatError with code UNKNOWN_EVENT_TYPE. A known type without a parser case in this SDK version is surfaced as UnknownEvent for forward-compatibility, and a known type with a malformed payload is preserved as a MalformedEvent (type: "malformed_event").

Session lifecycle events

interface RunStartedEvent   { type: "run_started";   sessionId: string; prompt: ContentInput }
interface RunCompletedEvent {
  type: "run_completed";
  sessionId: string;
  result: string;
  structuredOutput?: unknown;
  extractionRequired?: boolean;
  usage: Usage;
  terminalCauseKind?: TurnTerminalCauseKind;
}
interface ExtractionSucceededEvent {
  type: "extraction_succeeded";
  sessionId: string;
  structuredOutput: unknown;
  schemaWarnings?: readonly SchemaWarning[];
}
interface ExtractionFailedEvent {
  type: "extraction_failed";
  sessionId: string;
  lastOutput: string;
  attempts: number;
  reason: string;
}
interface RunFailedEvent {
  type: "run_failed";
  sessionId: string;
  errorClass: string;
  error: string;
  terminalCauseKind?: TurnTerminalCauseKind;
  errorReport?: AgentErrorReport | null;
}

Turn and LLM events

interface TurnStartedEvent        { type: "turn_started";         turnNumber: number }
interface TextDeltaEvent          { type: "text_delta";            delta: string }
interface TextCompleteEvent       { type: "text_complete";         content: string }
interface ToolCallRequestedEvent  { type: "tool_call_requested";   id: string; name: string; args: ToolCallArguments }
interface ToolResultReceivedEvent { type: "tool_result_received";  id: string; name: string; content: readonly ContentBlock[]; isError?: boolean }
interface TurnCompletedEvent      { type: "turn_completed";        stopReason?: StopReason; usage: Usage }
type StopReason =
  | "end_turn"
  | "tool_use"
  | "max_tokens"
  | "stop_sequence"
  | "content_filter"
  | "cancelled";

Tool execution events

interface ToolExecutionStartedEvent   { type: "tool_execution_started";    id: string; name: string }
interface ToolExecutionCompletedEvent { type: "tool_execution_completed";   id: string; name: string; content: readonly ContentBlock[]; isError?: boolean; durationMs?: number }
interface ToolExecutionTimedOutEvent  { type: "tool_execution_timed_out";   id: string; name: string; timeoutMs: number }

Compaction events

interface CompactionStartedEvent   { type: "compaction_started";   inputTokens: number; estimatedHistoryTokens: number; messageCount: number }
interface CompactionCompletedEvent { type: "compaction_completed";  summaryTokens: number; messagesBefore: number; messagesAfter: number }
// `reason` mirrors the Rust CompactionFailureReason tagged enum ({ kind, ... }).
interface CompactionFailedEvent    { type: "compaction_failed";     reason: Record<string, unknown> }

Budget events

interface BudgetWarningEvent { type: "budget_warning"; budgetType: BudgetType; used: number; limit: number; percent: number }

type BudgetType = "tokens" | "time" | "tool_calls";

Retry events

interface RetryingEvent { type: "retrying"; attempt: number; maxAttempts: number; error: string; delayMs: number }

Hook events

type HookPoint =
  | "run_started" | "run_completed" | "run_failed"
  | "pre_llm_request" | "post_llm_response"
  | "pre_tool_execution" | "post_tool_execution"
  | "turn_boundary";

interface HookStartedEvent        { type: "hook_started";         hookId: string; point: HookPoint }
interface HookCompletedEvent      { type: "hook_completed";        hookId: string; point: HookPoint; durationMs: number }
interface HookFailedEvent         { type: "hook_failed";           hookId: string; point: HookPoint; error: string }
interface HookDeniedEvent         { type: "hook_denied";           hookId: string; point: HookPoint; reasonCode: string; message: string; payload?: unknown }

Skill events

interface SkillsResolvedEvent       { type: "skills_resolved";         skills: readonly SkillKey[]; injectionBytes: number }
interface SkillResolutionFailedEvent { type: "skill_resolution_failed"; skillKey?: SkillKey; reason?: SkillResolutionFailureReason }
SkillResolutionFailureReason is a typed union discriminated on reasonType (not_found, capability_unavailable, load, parse, source_uuid_collision, source_uuid_mutation_without_lineage, missing_skill_remaps, remap_without_lineage, unknown_skill_alias, remap_cycle, unknown).

Comms events

interface InteractionCompleteEvent { type: "interaction_complete"; interactionId: string; result: string; structuredOutput?: unknown }
interface InteractionFailedEvent   { type: "interaction_failed";   interactionId: string; error: string }

Tool config events

interface ToolConfigChangedEvent { type: "tool_config_changed"; payload: ToolConfigChangedPayload }

// Payload keys intentionally remain wire-shaped (snake_case).
interface ToolConfigChangedPayload {
  operation?: "add" | "remove" | "reload";
  target?: string;
  status_info?: ToolConfigChangeStatus;
  persisted?: boolean;
  applied_at_turn?: number;
}

type ToolConfigChangeStatus =
  | { kind: "boundary_applied"; base_changed: boolean; visible_changed: boolean; revision: number }
  | { kind: "deferred_catalog_delta"; added_hidden_count: number; removed_hidden_count: number; pending_source_count: number }
  | { kind: "warning_failed_closed"; error: string }
  | { kind: "external_tool_delta"; phase: "pending" | "applied" | "draining" | "forced" | "failed"; detail?: string };

Background job events

type BackgroundJobTerminalStatus =
  | "completed"
  | "failed"
  | "aborted"
  | "cancelled"
  | "retired"
  | "terminated";

interface BackgroundJobCompletedEvent {
  type: "background_job_completed";
  jobId: string;
  displayName: string;
  terminalStatus: BackgroundJobTerminalStatus;
  detail: string;
}
terminalStatus is the typed semantic status; detail is the human-readable description.

Stream management

interface StreamTruncatedEvent { type: "stream_truncated"; reason: string }

Transcript rewrite events

interface TranscriptRewriteCommittedEvent {
  type: "transcript_rewrite_committed";
  sessionId: string;
  record: Record<string, unknown>;
}

Type guard utilities

The SDK exports type guards for the most commonly used events:
import {
  isTextDelta,
  isTextComplete,
  isTurnCompleted,
  isToolCallRequested,
  isRunCompleted,
  isRunFailed,
} from "@rkat/sdk";

for await (const event of session.stream("prompt")) {
  if (isTextDelta(event)) {
    process.stdout.write(event.delta);  // TypeScript knows event.delta exists
  }
  if (isTurnCompleted(event)) {
    console.log(event.stopReason, event.usage.inputTokens);
  }
}

Error handling

All errors extend MeerkatError. Catch it as a base class or use the specific subclasses for targeted handling.
import { MeerkatError, CapabilityUnavailableError } from "@rkat/sdk";

try {
  await client.connect();
  const session = await client.createSession("Hello");
  await session.turn("Next");
} catch (err) {
  if (err instanceof CapabilityUnavailableError) {
    console.error("Missing capability:", err.message, err.capabilityHint);
  } else if (err instanceof MeerkatError) {
    console.error(`[${err.code}] ${err.message}`);
  }
} finally {
  await client.close();
}

Error classes

class MeerkatError extends Error {
  readonly code: string;
  readonly details?: unknown;
  readonly capabilityHint?: { capability_id: string; message: string };
}

class CapabilityUnavailableError extends MeerkatError {}
class SessionNotFoundError extends MeerkatError {}
class SkillNotFoundError extends MeerkatError {}
ClassWhen thrown
MeerkatErrorBase class for all SDK errors
CapabilityUnavailableErrorclient.requireCapability() or session.invokeSkill() when the required capability is not available
SessionNotFoundErrorServer reports that the requested session does not exist
SkillNotFoundErrorServer reports that a skill reference cannot be resolved

Common error codes

CodeCause
NOT_CONNECTEDMethod called before connect()
VERSION_MISMATCHServer contract version incompatible with SDK
CAPABILITY_UNAVAILABLERequired capability not enabled in this build
CLIENT_CLOSEDMethod called after close()
STREAM_NOT_CONSUMEDstream.result accessed before iteration completed
BINARY_NOT_FOUNDrkat-rpc not found and auto-download failed
UNKNOWN_EVENT_TYPEStreamed event type not in the generated known-event inventory

Skills

Both createSession() and session.turn() accept structured skill parameters.
// Preload skills into the system prompt at session creation
const session = await client.createSession("Hello!", {
  preloadSkills: [
    { sourceUuid: "dc256086-0d2f-4f61-a307-320d4148107f", skillName: "extraction-email" },
    { sourceUuid: "dc256086-0d2f-4f61-a307-320d4148107f", skillName: "formatting-markdown" },
  ],
});

// Inject a skill for a specific turn via session.turn()
const result = await session.turn("Extract emails from this text", {
  skillRefs: [{ sourceUuid: "abc123", skillName: "extraction-email" }],
});

// Convenience method on Session — wraps turn() with requireCapability check
const result2 = await session.invokeSkill(
  { sourceUuid: "abc123", skillName: "extraction-email" },
  "Extract emails from this text",
);

Version compatibility

import { CONTRACT_VERSION } from "@rkat/sdk";
console.log(CONTRACT_VERSION);  // "0.7.11"
  • While the major version is 0, minor versions must match exactly between SDK and server.
  • From 1.0.0 onwards, standard semver applies: major versions must match.
Version checking happens automatically during connect(). A MeerkatError with code VERSION_MISMATCH is thrown if versions are incompatible.

See also