@zrt/js-sdk
v0.0.1-beta.1
Published
Build real-time AI voice agents in JavaScript / TypeScript. Zero Runtime runs the speech-to-speech pipeline (STT, LLM, TTS) for you.
Maintainers
Readme
ZRT — Zero Runtime JS SDK
Build real-time AI voice agents in JavaScript / TypeScript — without running the infrastructure. You write the agent (instructions, tools, logic); Zero Runtime runs the live speech-to-speech pipeline — speech-to-text → LLM → text-to-speech, with turn detection, denoising, and interruptions — at low latency in the cloud.
Write the agent. We run the runtime.
[!NOTE] Beta. This SDK is in public beta — the API may change in minor ways before 1.0. Pin a version and check the changelog when upgrading. Feedback is welcome.
The ZRT JS SDK — build voice agents in JavaScript / TypeScript with idiomatic conventions (ESM, a clean options-object API) over the ZRT gRPC wire contract.
A different kind of voice SDK
Most voice frameworks make you run the hard part — media servers, GPUs, turn-taking, autoscaling. No-code platforms hide all that but lock you into a dashboard. Zero Runtime is the middle: real TypeScript and your own providers, with none of the real-time infrastructure to operate.
| | Self-hosted frameworks | No-code platforms | Zero Runtime | |---|:---:|:---:|:---:| | Write real code + custom tools | ✅ | ❌ (dashboard) | ✅ | | Run media servers / GPUs / scaling | ❌ you run it | ✅ managed | ✅ managed | | Swap any STT / LLM / TTS provider | ✅ | limited | ✅ | | Low-latency speech-to-speech | you tune it | managed | managed |
Requirements
- Node.js 18+
- A ZRT runtime endpoint + auth token (from your Zero Runtime account)
- API key(s) for the providers you use (e.g. Deepgram, Google, Cartesia)
Install
npm install @zrt/js-sdkQuickstart
1. Set your environment
export ZRT_RUNTIME_ADDRESS=us1.rt.zeroruntime.ai:443 # your ZRT runtime
export ZRT_AUTH_TOKEN=<your-token>
export DEEPGRAM_API_KEY=<key> # speech-to-text
export GOOGLE_API_KEY=<key> # the LLM (Gemini)
export CARTESIA_API_KEY=<key> # text-to-speech2. Write your agent — agent.ts
import {
Agent,
AgentSession,
Pipeline,
WorkerJob,
JobContext,
RoomOptions,
EOUConfig,
InterruptConfig,
} from '@zrt/js-sdk/agents';
import { DeepgramSTT } from '@zrt/js-sdk/plugins/deepgram';
import { GoogleLLM } from '@zrt/js-sdk/plugins/google';
import { CartesiaTTS } from '@zrt/js-sdk/plugins/cartesia';
import { SileroVAD } from '@zrt/js-sdk/plugins/silero';
import { NamoTurnDetectorV1 } from '@zrt/js-sdk/plugins/turn_detector';
import { RNNoise } from '@zrt/js-sdk/plugins/rnnoise';
const IGNORE_PATTERNS = [String.raw`\b(uh+|um+)\b`]; // filler words to drop from transcripts
class Assistant extends Agent {
constructor() {
super('You are a friendly voice assistant. Keep replies short.');
}
async onEnter(): Promise<void> {
await this.session!.say('Hi! How can I help?');
}
async onExit(): Promise<void> {}
}
async function entrypoint(ctx: JobContext): Promise<void> {
const session = new AgentSession(
new Assistant(),
new Pipeline({
stt: new DeepgramSTT(),
llm: new GoogleLLM({
model: 'gemini-2.5-flash',
thinkingBudget: 0,
includeThoughts: false,
maxOutputTokens: 8192,
}),
tts: new CartesiaTTS(),
vad: new SileroVAD({ threshold: 0.4 }),
turnDetector: new NamoTurnDetectorV1(0.8, 'en'),
denoise: new RNNoise(),
eouConfig: new EOUConfig({ mode: 'ADAPTIVE', minMaxSpeechWaitTimeout: [0.1, 0.3] }),
interruptConfig: new InterruptConfig({
interruptMinDuration: 0.5,
interruptMinWords: 2,
resumeOnFalseInterrupt: true,
}),
sttFilterPatterns: IGNORE_PATTERNS,
sttWordSubstitutions: { recording: '', recorded: '' },
}),
);
await session.start({ waitForParticipant: true, runUntilShutdown: true });
}
new WorkerJob(
entrypoint,
() => new JobContext({ roomOptions: new RoomOptions({ name: 'Assistant' }) }),
).start();3. Run it
npx tsx agent.ts # or compile with tsc and run node agent.jsThat's it — speech in → your agent → speech out, in real time.
Examples
Full, runnable example agents live in a dedicated repo: ZeroRuntimeAI/zrt-js-sdk-examples.
How it works
| Piece | What it is |
|---|---|
| Agent | Your behavior — instructions, tools, what it says on enter/exit. |
| Pipeline | The voice stack: STT (hear) → LLM (think) → TTS (speak), plus VAD, turn detection, and denoising. |
| WorkerJob | Runs your agent and connects it to Zero Runtime. |
Give your agent tools
Let the LLM call your TypeScript functions. Node has no runtime type introspection,
so tools are declared explicitly with a name, description, JSON-schema parameters, and
an execute function:
import { functionTool } from '@zrt/js-sdk/agents';
const getWeather = functionTool({
name: 'get_weather',
description: 'Get the weather for a city.',
parameters: {
type: 'object',
properties: {
city: { type: 'string', description: 'City name' },
},
required: ['city'],
},
execute: async ({ city }: { city: string }) => ({ city, temp_c: 22 }),
});
// then pass them to your agent:
// super('...', { tools: [getWeather] })Your tool runs in your worker; the runtime calls it when the LLM decides to.
Providers
Mix and match — bring the best model for each stage, swap any one in a line:
- Speech-to-text (STT): Deepgram, AssemblyAI, Google, Azure, Gladia, NVIDIA, Sarvam
- LLM: OpenAI, Google Gemini, Anthropic Claude, Groq, Cerebras, xAI Grok, Sarvam, Cerebras, CometAPI
- Text-to-speech (TTS): Cartesia, ElevenLabs, Google, AWS Polly, Azure, Rime, LMNT, Neuphonic, Hume AI, Inworld, Murf, Resemble, Smallest, Speechify, CambAI, NVIDIA, Papla
- Realtime speech-to-speech: OpenAI Realtime, Gemini Live, Ultravox, Azure Voice Live, xAI
- Turn detection: Namo · VAD: Silero · Denoise: RNNoise
import { ElevenLabsTTS } from '@zrt/js-sdk/plugins/elevenlabs'; // different TTS
import { AnthropicLLM } from '@zrt/js-sdk/plugins/anthropic'; // different LLMEach provider package lives under the @zrt/js-sdk/plugins/<name> subpath and is imported from
there.
Use cases
Phone & telephony agents, IVR replacement, customer-support voice bots, voice assistants, outbound/inbound call automation, and any real-time conversational AI.
API conventions
This SDK follows idiomatic JavaScript / TypeScript conventions:
- A consistent public API: every option uses the same naming style.
- Constructors take a single options object
(
new GoogleLLM({ maxOutputTokens: 8192 })). - Tools are declared with an explicit
functionTool({ name, description, parameters, execute })descriptor. - Wire strings follow the runtime contract — proto fields, runtime-config keys, event names, and gRPC headers are sent exactly as the ZRT runtime expects.
FAQ
How is this different from a voice-agent framework?
- Frameworks make you host and scale the real-time runtime (media, GPUs, turn-taking). ZRT runs that for you — you only write and deploy the agent.
How is it different from a no-code voice platform?
- You write real TypeScript with your own tools, logic, and providers — not a dashboard configuration. Full code control, zero infrastructure.
Can I use my own STT / LLM / TTS providers?
- Yes — mix any supported providers, and bring your own API keys.
What do I need to run it?
- A ZRT runtime endpoint + token and the provider keys for the stages you use.
Contact
Copyright © 2026 Zujo Tech Pvt Ltd. All rights reserved.
