Skip to content

Agent SDK

@rozenite/agent-sdk is the programmatic version of rozenite agent for Node.js and TypeScript scripts. Use it when you want to write a script, automation, or agent runtime that talks to a running app through Rozenite directly from code.

Make sure:

  • Node.js 20 or newer
  • your app is running in development mode
  • Metro is running and reachable
  • at least one target is connected

Install the SDK as a dependency:

npm install @rozenite/agent-sdk

Most scripts use the same flow:

  1. Create a client with createAgentClient().
  2. Open a short-lived session with client.withSession(...).
  3. Inspect the available domains and tools.
  4. Call the tool you need.

Start with a script that opens a session and lists the domains currently available on the device:

agent-sdk-first-script.ts
import { createAgentClient } from '@rozenite/agent-sdk';
async function inspectApp() {
const client = createAgentClient();
return client.withSession(async (session) => {
const domains = await session.domains.list();
return {
sessionId: session.id,
domains: domains.map((domain) => domain.id),
};
});
}
inspectApp().then(console.log).catch(console.error);

withSession(...) opens the session and closes it automatically when your callback finishes.

The next step is to inspect one domain more closely and then call one of its tools.

Inside a session, you can use these helpers:

  • session.domains.list() to see which domains are available on the connected app
  • session.tools.list({ domain }) to see the tools inside one domain
  • session.tools.getSchema({ domain, tool }) to inspect a tool’s input schema before calling it

For example, you can inspect the network domain like this:

agent-sdk-inspect-tools.ts
import { createAgentClient } from '@rozenite/agent-sdk';
async function inspectNetworkTools() {
const client = createAgentClient();
return client.withSession(async (session) => {
const tools = await session.tools.list({ domain: 'network' });
const schema = await session.tools.getSchema({
domain: 'network',
tool: 'listRequests',
});
return {
toolNames: tools.map((tool) => tool.shortName),
listRequestsInput: schema.inputSchema,
};
});
}

Once you know what you want to call, use session.tools.call(...).

There are two common ways to call a tool.

Use this form when you already know the domain and tool name, or when you just looked them up with session.tools.list(...).

If you want a typed result, pass type arguments to session.tools.call(...).

agent-sdk-call-by-name.ts
const requests = await session.tools.call<{ limit: number }, { items: Array<{ id: string }> }>({
domain: 'network',
tool: 'listRequests',
args: { limit: 20 },
});

If you do not pass type arguments, the result is unknown.

Official agent-enabled plugins publish ready-made typed tool descriptors under ./sdk. Use those when you want the plugin package to provide the tool name, argument type, and result type for you.

Today that includes:

  • @rozenite/controls-plugin/sdk
  • @rozenite/file-system-plugin/sdk
  • @rozenite/network-activity-plugin/sdk
  • @rozenite/react-navigation-plugin/sdk
  • @rozenite/redux-devtools-plugin/sdk
  • @rozenite/storage-plugin/sdk
  • @rozenite/tanstack-query-plugin/sdk
agent-sdk-typed-call.ts
import { createAgentClient } from '@rozenite/agent-sdk';
import { storageTools } from '@rozenite/storage-plugin/sdk';
async function readUsernameFromStorage() {
const client = createAgentClient();
return client.withSession(async (session) => {
const result = await session.tools.call(storageTools.readEntry, {
adapterId: 'mmkv',
storageId: 'user-storage',
key: 'username',
});
return result;
});
}

Some tools return paged results. The SDK makes one tool call and returns the plugin’s page and cursor unchanged. To fetch another page, pass the returned cursor explicitly in the next call.

agent-sdk-pagination.ts
type RequestsPage = {
items: Array<{ id: string }>;
page: { limit: number; hasMore: boolean; nextCursor?: string };
};
const firstPage = await session.tools.call<{ limit: number; cursor?: string }, RequestsPage>({
domain: 'network',
tool: 'listRequests',
args: { limit: 50 },
});
const secondPage = firstPage.page.nextCursor
? await session.tools.call<{ limit: number; cursor?: string }, RequestsPage>({
domain: 'network',
tool: 'listRequests',
args: { limit: 50, cursor: firstPage.page.nextCursor },
})
: undefined;

If only one target is connected, withSession(...) is usually enough.

If more than one target is connected, list them first and pass deviceId when opening the session:

agent-sdk-target-selection.ts
import { createAgentClient } from '@rozenite/agent-sdk';
async function inspectSpecificDevice() {
const client = createAgentClient();
const targets = await client.targets.list();
return client.withSession({ deviceId: targets[0].id }, async (session) => {
return {
sessionId: session.id,
deviceId: session.info.deviceId,
};
});
}
  • Use withSession(...) for short scripts and one-off tasks. It handles setup and cleanup for you.
  • Use openSession() when the session needs to stay open across multiple independent steps or function boundaries.
  • Use attachSession(sessionId) when you need to reconnect to an already-existing session instead of creating a new one.

When you do need manual control, remember to stop the session yourself:

agent-sdk-manual-session.ts
import { createAgentClient } from '@rozenite/agent-sdk';
async function inspectRozeniteManually() {
const client = createAgentClient();
const session = await client.openSession();
try {
const domains = await session.domains.list();
return {
session: session.info,
domains,
};
} finally {
await session.stop();
}
}

If you already have a sessionId, you can reconnect like this:

agent-sdk-attach-session.ts
import { createAgentClient } from '@rozenite/agent-sdk';
async function reconnectToSession(sessionId: string) {
const client = createAgentClient();
const session = await client.attachSession(sessionId);
return {
sessionId: session.id,
deviceId: session.info.deviceId,
};
}