Home/Documentation
Voice Memory · JavaScript SDK

JS / TypeScript SDK reference

Server-side TypeScript SDKs for LiveKit Agents and custom Node voice workers. MemoAir memory runs in the Node worker next to STT, LLM, TTS, and VAD. Browser clients join the LiveKit room; they do not run the MemoAir memory runtime.

Install

terminal
BASH
# Voice worker
npm install memoair-voice memoair-livekit @livekit/agents
 
# Optional partner/backend provisioning API
npm install memoair-admin

memoair-livekit depends on memoair-voice. Install both when you also use the raw client for indexing, smoke tests, or custom framework code.

Provision projects from code

Create one MemoAir project per customer and one agent per customer-facing voice flow before starting the LiveKit worker. This is a server-side admin operation using your account-scoped memoair_pk_* key, not a browser call.

provisionCustomer.ts
TS
import { MemoAirAdmin } from "memoair-admin"
 
const admin = new MemoAirAdmin({
apiKey: process.env.MEMOAIR_API_KEY!, // memoair_pk_*
})
 
// 1. Create one project per customer.
const project = await admin.createProject({
name: "Customer A",
slug: "customer-a",
description: "Provisioned from onboarding flow",
})
 
// 2. Create a voice agent inside that project.
const agent = await admin.createAgent({
name: "Outbound Sales Agent",
projectId: project.id,
agentType: "voice",
})
 
// 3. Store these IDs in your own DB. The LiveKit worker uses them later.
console.log({
memoairProjectId: project.id,
memoairAgentId: agent.id,
})

Backend endpoints: POST /v1/projects and POST /v1/projects/{projectId}/agents. The API key's org is inferred server-side and remains the tenancy boundary; a key from org A cannot create agents in org B or attach agents to org B projects.

MemoAirVoiceClient

High-level Node client from memoair-voice. Construct it once at process boot. Per-call userId or user={ id } routes each turn to the correct local runtime.

agent.ts
TS
import { MemoAirVoiceClient } from "memoair-voice"

Constructor

client.ts
TS
const client = new MemoAirVoiceClient({
apiKey: process.env.MEMOAIR_API_KEY!,
projectId: process.env.MEMOAIR_PROJECT_ID!,
agentId: process.env.MEMOAIR_AGENT_ID!,
})

Parameters

apiKeystringRequired

MemoAir account API key (memoair_pk_...). Used for cloud index calls and passed to every spawned local runtime for bootstrap/sync.

projectIdstringRequired

MemoAir project/workspace ID. Selects the org index, profile, permanent lane, and runtime storage partition.

agentIdstringRequired

MemoAir agent ID. Sent as X-Agent-Id on cloud calls so dashboard traces and agent configuration stay scoped.

searchMemory(query, options)

Local recall across profile, working, permanent, and org lanes. Returns a composed contextText string plus structured hits and trace timings.

search.ts
TS
const result = await client.searchMemory("where do I live?", {
user: { id: "caller_42", name: "Alex" },
lanes: ["profile", "working", "permanent", "org"],
timeoutMs: 250,
})
 
console.log(result.contextText)
console.log(result.trace.totalMs)

Parameters

user / userId{ id, name?, metadata? } | stringRequired

End-user identity. Same ID across calls means the same per-user working/permanent memory lane.

lanesstring[]Optional

Subset of ["profile", "working", "permanent", "org"]. Defaults to all lanes.

intentstringOptionalDefault: "answer_current_user"

Advisory hint forwarded to the local composer. The default is correct for most voice turns.

topKRecord<string, number>Optional

Per-lane top-k override, for example { permanent: 6, org: 4, working: 4 }.

timeoutMsnumberOptionalDefault: 250

Per-turn memory deadline. Recall is local; late lanes are dropped rather than blocking the voice path.

saveResponse(options)

Persist a completed user/assistant turn. The local runtime appends immediately and syncs to cloud asynchronously.

save.ts
TS
await client.saveResponse({
userText: "I live in Bengaluru.",
assistantText: "Got it.",
userId: "caller_42",
metadata: { framework: "livekit" },
})

createIndex(name, documents, options)

Cloud-side build-or-append for the project org index. Use this for small structured text seeds and use the dashboard for PDFs, website imports, or larger corpora.

buildIndex.ts
TS
await client.createIndex(
"agent-memory",
[
{
id: "return-policy",
text: "Returns are allowed within 30 days with a receipt.",
metadata: { kind: "faq", topic: "returns" },
},
],
{ userId: "index-builder" },
)

Parameters

namestringRequired

Stable org index name. First call creates the index; later calls append or replace documents by ID.

documentsArray<{ id: string; text: string; metadata?: object }>Required

Plain text documents. Keep this SDK path for small code-driven seeds; use the dashboard for PDFs, site imports, or large batches.

userIdstringOptional

Project-key callers should stamp a stable user ID such as index-builder. Org-only clients stamp the org sentinel automatically.

MemoAirLiveKitAgent

Drop-in @livekit/agents voice.Agent subclass from memoair-livekit. It opens a pinned runtime session on enter, searches before each reply, injects recall as a system message, and saves each assistant turn when you call registerSessionEvents(session).

agent.ts
TS
import { MemoAirLiveKitAgent } from "memoair-livekit"
agent.ts
TS
const agent = new MemoAirLiveKitAgent({
apiKey: process.env.MEMOAIR_API_KEY!,
projectId: process.env.MEMOAIR_PROJECT_ID!,
agentId: process.env.MEMOAIR_AGENT_ID!,
userId: participantIdentity,
instructions:
"You are a helpful voice assistant. Relevant memories are injected " +
"as a system message before each reply.",
})
 
const session = new voice.AgentSession({
vad: ctx.proc.userData.vad as silero.VAD,
stt: new deepgram.STT({ model: "nova-2", language: "multi" }),
llm: new openai.LLM({ model: "gpt-4o-mini" }),
tts: new openai.TTS({ model: "gpt-4o-mini-tts", voice: "shimmer" }),
})
 
agent.registerSessionEvents(session)
ctx.addShutdownCallback(() => agent.onExit())
await session.start({ agent, room: ctx.room })

Server-side worker, not browser runtime

Put memoair-livekit inside the TypeScript LiveKit Agent worker. Do not import it in a Next.js client component or browser participant. The browser owns mic/playback/UI; the worker owns STT, LLM, TTS, VAD, and MemoAir memory.

Result and document shapes

types.ts
TS
type IndexDocument = {
id: string
text: string
metadata?: Record<string, unknown>
}
 
type SearchResult = {
contextText: string
profile: Record<string, unknown> | null
working: Record<string, unknown>[]
permanent: Record<string, unknown>[]
org: Record<string, unknown>[]
sources: Record<string, unknown>[]
trace: Record<string, unknown>
}
 
type IndexBuildResult = {
indexName: string
chunkCount: number
version: number | null
}