@smallest-ai/agent-sdk
v0.3.0
Published
SDK for connecting to Smallest AI voice agents — no WebRTC required
Maintainers
Readme
Atoms Agent SDK
Connect to your Atoms voice agent with a few lines of code. The SDK handles microphone capture, audio playback, and WebSocket communication — you just listen for events.
Installation
npm install @smallest-ai/agent-sdkOr use the CDN in a browser:
<script src="https://cdn.jsdelivr.net/npm/@smallest-ai/agent-sdk/dist/agent-sdk.browser.js"></script>Quick Start
npm / Bundler
import { AtomsAgent } from "@smallest-ai/agent-sdk";
const agent = new AtomsAgent({
apiKey: "sk_...",
agentId: "your_agent_id",
});
agent.on("session_started", (e) => console.log("Call ID:", e.call_id));
agent.on("transcript", (e) => console.log(`${e.role}: ${e.text}`));
agent.on("agent_start_talking", () => console.log("Agent speaking..."));
agent.on("agent_stop_talking", () => console.log("Agent stopped"));
agent.on("session_ended", (e) => console.log("Ended:", e.reason));
await agent.connect();
// Microphone is automatically captured.
// Speak and the agent will respond through your speakers.
// When done:
agent.disconnect();Browser (CDN)
<script src="https://cdn.jsdelivr.net/npm/@smallest-ai/agent-sdk/dist/agent-sdk.browser.js"></script>
<script>
const agent = new AtomsSdk.AtomsAgent({
apiKey: "sk_...",
agentId: "your_agent_id",
});
agent.on("transcript", (e) => console.log(`${e.role}: ${e.text}`));
agent.on("agent_start_talking", () => console.log("Agent speaking..."));
agent.on("agent_stop_talking", () => console.log("Agent stopped"));
agent.connect();
</script>Security
Important: The token is sent as a URL query parameter (
?token=…) during the WebSocket handshake. Browser WebSocket does not support custom headers, so this is unavoidable. The token will appear in server access logs and proxy logs.Recommended approach: Use the
/conversation/register-callendpoint server-side to get a short-lived access token (30s TTL, single-use). The raw API key stays on your server; the browser only ever sees the token.Your Server → POST /conversation/register-call { agent_id } (uses your API key) ← { access_token: "wct_…", call_id, expires_in: 30 } Browser → new AtomsAgent({ apiKey: access_token, agentId }).connect() (?token=wct_… in upgrade request — expires in 30s, single-use)
Configuration
| Option | Type | Default | Description |
| ---------------- | --------------------------------------------- | ------- | ----------------------------------------------------------------------- |
| apiKey | string | — | Your Atoms API key (or short-lived access token — see Security) |
| agentId | string | — | The agent ID to connect to |
| variables | Record<string, string \| number \| boolean> | — | Per-call prompt variables (see Dynamic Variables) |
| sampleRate | number | 24000 | Audio sample rate in Hz |
| autoCaptureMic | boolean | true | Capture the microphone automatically on connect |
| baseUrl | string | prod | Override the WebSocket base URL (local development only) |
Methods
connect()
Connect to the agent and start a session. If autoCaptureMic is true, the microphone starts immediately.
await agent.connect();disconnect()
End the session and clean up all resources (microphone, audio playback, WebSocket).
agent.disconnect();sendText(text)
Send a text message to the agent (chat mode).
agent.sendText("Hello, I need help with my account.");mute()
Stop sending microphone audio to the agent. The connection stays open.
agent.mute();unmute()
Resume sending microphone audio.
agent.unmute();isConnected
Returns true if connected to the agent.
if (agent.isConnected) { ... }isMuted
Returns true if the microphone is muted.
if (agent.isMuted) { ... }Events
Listen for events using agent.on(eventName, callback).
session_started
Fired when the session is created and ready.
agent.on("session_started", (event) => {
console.log("Session ID:", event.session_id);
console.log("Call ID:", event.call_id);
});session_ended
Fired when the session ends (agent-initiated or client-initiated). After this event the agent is disconnected and resources are released.
agent.on("session_ended", (event) => {
console.log("Ended:", event.reason);
// reason: "client_requested", "websocket_closed", "ended", etc.
});transcript
Fired after each completed speech turn with the final transcription text. Fires for both user and agent turns.
agent.on("transcript", (event) => {
console.log(event.role); // "user" | "assistant"
console.log(event.text); // final transcript text
});| Field | Type | Description |
| ------ | ------------------------- | ------------------------------------------- |
| role | "user" | "assistant" | Who spoke — the user or the agent |
| text | string | The complete transcribed text for this turn |
agent_start_talking
Fired when the agent begins speaking.
agent.on("agent_start_talking", () => {
showStatus("Agent speaking...");
});agent_stop_talking
Fired when the agent finishes speaking.
agent.on("agent_stop_talking", () => {
showStatus("Listening...");
});error
Fired when a protocol or server error occurs.
agent.on("error", (event) => {
console.error(`[${event.code}] ${event.message}`);
});Dynamic Variables
Pass per-call values to your agent prompt using {{variable_name}} placeholders. Values supplied here override the agent's configured defaults for this session only.
const agent = new AtomsAgent({
apiKey: "sk_...",
agentId: "...",
variables: {
customer_name: "Tanay",
account_tier: "gold",
outstanding_balance: 1500,
},
});
await agent.connect();Rules:
- Values must be
string,number, orboolean. Objects and arrays are rejected. - Variable keys are matched against
{{key}}placeholders in the agent prompt. Unknown keys are ignored. - Reserved keys (
call_id,conversation_type) cannot be overridden. - Each
connect()opens a fresh session with the variables set at construction time. To use different variables, create a newAtomsAgent.
Examples
Show Transcript
const agent = new AtomsAgent({ apiKey: "sk_...", agentId: "..." });
agent.on("transcript", (e) => {
const bubble = document.createElement("div");
bubble.className = `turn ${e.role}`;
bubble.textContent = `${e.role}: ${e.text}`;
document.getElementById("transcript").appendChild(bubble);
});
await agent.connect();UI Integration
const agent = new AtomsAgent({ apiKey: "sk_...", agentId: "..." });
agent.on("session_started", () => setStatus("Connected"));
agent.on("agent_start_talking", () => setStatus("Agent speaking..."));
agent.on("agent_stop_talking", () => setStatus("Listening..."));
agent.on("transcript", (e) => appendTranscript(e.role, e.text));
agent.on("error", (e) => setStatus(`Error: ${e.message}`));
agent.on("session_ended", () => setStatus("Disconnected"));
connectBtn.onclick = () => agent.connect();
disconnectBtn.onclick = () => agent.disconnect();
muteBtn.onclick = () => (agent.isMuted ? agent.unmute() : agent.mute());Manual Microphone Control
const agent = new AtomsAgent({
apiKey: "sk_...",
agentId: "...",
autoCaptureMic: false, // don't start mic on connect
});
await agent.connect();
// Start mic only when user clicks
startMicBtn.onclick = () => agent.unmute();
muteMicBtn.onclick = () => agent.mute();Audio Format
All audio is handled internally. If you are building a custom integration directly against the WebSocket protocol:
| Property | Value | | ----------- | -------------------------------- | | Encoding | PCM 16-bit signed, little-endian | | Sample rate | 24,000 Hz (configurable) | | Channels | 1 (mono) | | Transport | Base64-encoded JSON |
Error Handling
Authentication and validation errors close the WebSocket immediately with a 4xxx code:
agent.on("error", (e) => {
// e.code — server-defined error code string
// e.message — human-readable description
});
agent.on("session_ended", (e) => {
// e.reason — close reason string
});Common close reasons: "invalid_token", "agent_not_found", "credits_exhausted", "ended".
Browser Support
Requires standard Web APIs available in all modern browsers (Chrome, Firefox, Safari, Edge):
WebSocketgetUserMediaAudioContext/ScriptProcessorNode
Limits
| Limit | Value | | -------------------- | -------------------------------------------- | | Max session duration | 15 minutes | | Audio format | PCM 16-bit, 24kHz, mono | | Access token TTL | 30 seconds (if using server-side token flow) |
