Clients
Client SDK
Call an agent from your backend, scripts, or any JavaScript runtime, and stream its events.
Call an agent from your backend, a script, or any JavaScript runtime with rivetkit/client.
import { ActorError, createClient } from "rivetkit/client";
import type { registry } from "./server";
const client = createClient<typeof registry>();
const agent = client.agent.getOrCreate(["support", "customer-123"]);
const history = await agent.getMessages();
console.log(`${history.length} messages so far`);
const conn = agent.connect();
conn.onStatusChange((status) => console.log(`[connection ${status}]`));
const unsubscribe = conn.on("event", (event) => {
switch (event.type) {
case "message_update":
if (event.assistantMessageEvent.type === "text_delta") {
process.stdout.write(event.assistantMessageEvent.delta);
}
break;
case "tool_execution_start":
console.log(`\n> ${event.toolName}`);
break;
case "tool_execution_end":
console.log(event.isError ? " failed" : " done");
break;
case "auto_retry_start":
console.log(`\nRetrying (${event.attempt}/${event.maxAttempts}): ${event.errorMessage}`);
break;
}
});
try {
await conn.prompt("Summarize the README.");
} catch (error) {
if (!(error instanceof ActorError)) throw error;
console.error(`\n${error.group}.${error.code}: ${error.message}`);
}
const last = (await conn.getMessages()).at(-1);
if (last?.role === "assistant" && last.stopReason === "error") {
console.error(`\nModel error: ${last.errorMessage}`);
}
unsubscribe();
await conn.dispose();
import { pi } from "@rivet-dev/pi";
import { agentOSProvider } from "@rivet-dev/sandbox-adapter/agentos";
import { setup } from "rivetkit";
const agent = pi({
model: "anthropic/claude-opus-5-5",
sandbox: agentOSProvider(),
});
export const registry = setup({ use: { agent } });
registry.start();
Handles and connections
createClient()readsRIVET_ENDPOINT,RIVET_NAMESPACE, andRIVET_TOKENfrom the environment, and defaults to a local Rivet atlocalhost:6420.getOrCreatereturns a handle to the agent with that key, and creates the agent on first use. Actions called on the handle, likegetMessages, are single requests.connect()opens a live connection that receives the session’s events. Actions called before it opens wait until it does.- A dropped connection reconnects on its own.
onStatusChangereportsconnecting,connected, anddisconnected, andidleafterdispose()closes the connection for good.
Events
on("event", ...) receives every event of the session as the agent works. See Session Lifecycle for the event types.
Results and errors
promptresolves when the run ends. Read the reply withgetLastAssistantTextorgetMessages.promptrejects with anActorErrorwhen the action itself fails. For example, another prompt is already running (passstreamingBehaviorto queue it instead), the model has no credential (model_unavailable), or the run passes the ten-minute action timeout (action_timed_out).- A model error doesn’t reject. The run ends with an assistant message whose
stopReasonis"error", anderrorMessageexplains why.
Browsers
A browser shouldn’t hold your Rivet credentials. Your backend checks who the user is, then mints a short-lived token that reaches only their agent.
import { createClient } from "rivetkit/client";
import type { registry } from "./server";
async function fetchAgentToken(): Promise<{ agentId: string; token: string }> {
const response = await fetch("/agent-token", { method: "POST", cache: "no-store" });
if (!response.ok) throw new Error("Could not get an agent token.");
return (await response.json()) as { agentId: string; token: string };
}
const { agentId } = await fetchAgentToken();
const client = createClient<typeof registry>({
endpoint: "https://api.rivet.dev",
namespace: "production",
getToken: async () => (await fetchAgentToken()).token,
});
const conn = client.agent.getForId(agentId).connect();
await conn.prompt("Summarize the README.");
console.log(await conn.getLastAssistantText());
import { Hono } from "hono";
import { createClient } from "rivetkit/client";
import type { registry } from "./server";
const admin = createClient<typeof registry>();
async function authenticateUser(request: Request): Promise<string | null> {
return request.headers.get("x-user-id");
}
const app = new Hono();
app.post("/agent-token", async (c) => {
const userId = await authenticateUser(c.req.raw);
if (!userId) return c.json({ error: "unauthorized" }, 401);
const agent = admin.agent.getOrCreate(["support", userId]);
const { token } = await agent.issueToken({ subject: userId, expiresIn: 900 });
return c.json({ agentId: await agent.resolve(), token }, 200, { "Cache-Control": "no-store" });
});
export default app;
token.tsresolves the user’s agent and callsissueToken, which returns a token for that one agent that expires in 15 minutes.browser.tsconnects withgetForId.getTokenfetches a new token whenever the old one expires, so the connection stays up.
See JWTs and Authentication.
The same actions work over HTTP. See the curl tab in the Quickstart.
See the client documentation in the Actors docs for everything rivetkit/client can do.