All public types use camelCase field names. The SDK translates to and from the snake_case JSON-RPC wire format internally.
Imports
Everything public is re-exported from the package root:
import {
MeerkatClient,
Session,
Mob,
EventStream,
EventSubscription,
MeerkatError,
CapabilityUnavailableError,
SessionNotFoundError,
SkillNotFoundError,
CONTRACT_VERSION,
} from "@rkat/sdk";
import type {
RunResult,
Usage,
SessionInfo,
SessionOptions,
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";
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 structuredOutput?: unknown;
readonly schemaWarnings?: readonly SchemaWarning[];
readonly skillDiagnostics?: SkillRuntimeDiagnostics;
}
| Field | Type | Description |
|---|
sessionId | string | Stable UUID for this session |
sessionRef | string | undefined | Optional human-readable reference |
text | string | Full assistant text from the last LLM turn |
turns | number | Number of LLM turns in this run |
toolCalls | number | Total tool calls executed in this run |
usage | Usage | Token usage for this run |
structuredOutput | unknown | undefined | Parsed structured output when outputSchema was provided |
schemaWarnings | SchemaWarning[] | undefined | Warnings emitted when structured output didn’t fully match the schema |
skillDiagnostics | SkillRuntimeDiagnostics | undefined | Runtime 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(...).
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: string;
readonly updatedAt: string;
readonly messageCount: number;
readonly totalTokens: number;
readonly isActive: boolean;
}
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 };
| Variant | Fields | Description |
|---|
text | text: string | Plain text content |
image | media_type: string, data: string | Base64-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. Preferred over legacy string references.
interface SkillKey {
readonly sourceUuid: string;
readonly skillName: string;
}
SkillRef
A skill reference is either a SkillKey or a legacy string in the form "<source_uuid>/<skill_name>". Legacy strings emit a deprecation warning.
type SkillRef = SkillKey | string;
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
| RunFailedEvent
| TurnStartedEvent
| TextDeltaEvent
| TextCompleteEvent
| ToolCallRequestedEvent
| ToolResultReceivedEvent
| TurnCompletedEvent
| ToolExecutionStartedEvent
| ToolExecutionCompletedEvent
| ToolExecutionTimedOutEvent
| CompactionStartedEvent
| CompactionCompletedEvent
| CompactionFailedEvent
| BudgetWarningEvent
| RetryingEvent
| HookStartedEvent
| HookCompletedEvent
| HookFailedEvent
| HookDeniedEvent
| HookRewriteAppliedEvent
| HookPatchPublishedEvent
| SkillsResolvedEvent
| SkillResolutionFailedEvent
| InteractionCompleteEvent
| InteractionFailedEvent
| StreamTruncatedEvent
| ToolConfigChangedEvent
| UnknownEvent;
Unknown event types are surfaced as UnknownEvent for forward-compatibility.
Session lifecycle events
interface RunStartedEvent { type: "run_started"; sessionId: string; prompt: string }
interface RunCompletedEvent { type: "run_completed"; sessionId: string; result: string; usage: Usage }
interface RunFailedEvent { type: "run_failed"; sessionId: string; error: string }
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: unknown }
interface ToolResultReceivedEvent { type: "tool_result_received"; id: string; name: string; isError: boolean }
interface TurnCompletedEvent { type: "turn_completed"; stopReason: StopReason; usage: Usage }
type StopReason =
| "end_turn"
| "tool_use"
| "max_tokens"
| "stop_sequence"
| "content_filter"
| "cancelled";
interface ToolExecutionStartedEvent { type: "tool_execution_started"; id: string; name: string }
interface ToolExecutionCompletedEvent { type: "tool_execution_completed"; id: string; name: string; result: string; 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 }
interface CompactionFailedEvent { type: "compaction_failed"; error: string }
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 }
interface HookRewriteAppliedEvent { type: "hook_rewrite_applied"; hookId: string; point: HookPoint; patch: Record<string, unknown> }
interface HookPatchPublishedEvent { type: "hook_patch_published"; hookId: string; point: HookPoint; envelope: Record<string, unknown> }
Skill events
interface SkillsResolvedEvent { type: "skills_resolved"; skills: readonly string[]; injectionBytes: number }
interface SkillResolutionFailedEvent { type: "skill_resolution_failed"; reference: string; error: string }
Comms events
interface InteractionCompleteEvent { type: "interaction_complete"; interactionId: string; result: string }
interface InteractionFailedEvent { type: "interaction_failed"; interactionId: string; error: string }
interface ToolConfigChangedEvent { type: "tool_config_changed"; payload: ToolConfigChangedPayload }
interface ToolConfigChangedPayload {
operation: "add" | "remove" | "reload";
target: string;
status: string;
persisted: boolean;
appliedAtTurn?: number;
}
Stream management
interface StreamTruncatedEvent { type: "stream_truncated"; reason: string }
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 {}
| Class | When thrown |
|---|
MeerkatError | Base class for all SDK errors |
CapabilityUnavailableError | client.requireCapability() or session.invokeSkill() when the required capability is not available |
SessionNotFoundError | Server reports that the requested session does not exist |
SkillNotFoundError | Server reports that a skill reference cannot be resolved |
Common error codes
| Code | Cause |
|---|
NOT_CONNECTED | Method called before connect() |
VERSION_MISMATCH | Server contract version incompatible with SDK |
CAPABILITY_UNAVAILABLE | Required capability not enabled in this build |
CLIENT_CLOSED | Method called after close() |
STREAM_NOT_CONSUMED | stream.result accessed before iteration completed |
BINARY_NOT_FOUND | rkat-rpc not found and auto-download failed |
Skills
Both createSession() and session.turn() accept skill parameters. Use SkillKey objects (preferred) or legacy strings.
// Preload skills into the system prompt at session creation
const session = await client.createSession("Hello!", {
preloadSkills: ["extraction/email", "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); // e.g. "0.5.0"
- 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