◢VoiceLayer
Dashboard

Build / Brains in code

Your agent is the brain

Keep your own model, your own agent framework, your own memory. VoiceLayer keeps speech-to-text, text-to-speech, turn-taking, telephony and the transcript; your worker serves the thinking over the same socket as your tools.

Three ways to bring a brain

onQuery

A function: the latest utterance and the text history in, a string or an async iterable of strings out. Text only.

brain: openaiAgentsBrain / claudeAgentBrain / langgraphBrain

A framework adapter. Cancel is wired, VoiceLayer’s tools reach the framework as its own, text streams as it arrives.

A direct or tunnel brain connector

No code in your worker: register an endpoint (or run the vl-brain daemon behind your firewall) and bind it in the dashboard. See brain connectors.

onQuery text

Your function IS the model. It is handed the caller’s latest words and the text history, and returns the reply — a string, or an async iterable to stream, so the first sentence is spoken as soon as it lands.

agent.ts
import { defineAgent } from '@voicelayer/sdk/v1';

export default defineAgent({
  name: 'support',
  prompt: 'You answer for Acme.',
  // a string, or an async iterable of strings to stream
  onQuery: async (text, { messages, signal }) => myAgent.reply(text, messages, { signal })
});
✋

Honour signal.It aborts when the caller barges in, or the call ends. A brain that keeps running after that keeps spending your tokens. The next turn’s history already carries only what the caller actually heard of the interrupted reply.

A framework brain that uses VoiceLayer’s tools

onQuery is text only. When the brain needs call control (transfer, hang up, send keypad digits) or your registry tools, use an adapter. It registers the turn’s tools as the framework’s own; a tool call crosses to VoiceLayer and runs under the same Action Guard as a tool the platform’s own model would call, and a tool whose capability the agent doesn’t declare is never offered.

agent.ts
import { Agent, run, tool } from '@openai/agents';
import { defineAgent, openaiAgentsBrain } from '@voicelayer/sdk/v1';

export default defineAgent({
  name: 'harbor-brain',
  prompt: 'You are the front desk of Harbor Dental.',
  capabilities: ['system.say', 'system.transfer_call', 'system.end_call'],
  toolBindings: [], // none on 1.0; on 0.x [] still attaches every registry tool, so name the ones you use

  brain: openaiAgentsBrain({
    run, // the framework's own run: run(agent, input, { stream: true, signal })
    // the Agent for THIS turn, with VoiceLayer's tools as its function tools
    agent: (voicelayerTools) =>
      new Agent({
        name: 'front-desk',
        model: 'gpt-4o-mini',
        instructions: 'Short, natural sentences. If someone wants a person, use transfer_call.',
        tools: voicelayerTools.map((t) =>
          tool({
            name: t.name,
            description: t.description,
            parameters: t.parameters as never,
            strict: false,
            execute: async (args) => JSON.stringify(await t.execute(args as Record<string, unknown>))
          })
        )
      })
  })
});

Adapters exist for the OpenAI Agents SDK, the Claude Agent SDK and LangGraph. They touch the framework through its documented shape only, so the SDK ships no framework: pin the framework’s version and run the call once in the Playground when you upgrade it.

What is yours, and what is guarded

The perimeter is every conversation VoiceLayer runs. A customer brain’s internals and a call handed off at SIP are outside it; VoiceLayer guards what goes to a brain, what comes back, and every action it asks for. In practice: the caller’s number reaches your brain as a placeholder; what the brain says passes the output guard before anything is spoken; every tool call is a request VoiceLayer checks. The brain’s own model usage is on your account and is never billed by VoiceLayer.

Test a brain with no network

brain.test.ts
import { runBrain } from '@voicelayer/sdk/testing';

const out = await runBrain(agent, { messages: [{ role: 'user', content: 'what time do you open?' }] });
expect(out.text).toContain('nine');

runBrain runs the brain for one turn exactly as the worker would on a turn from VoiceLayer. End to end is voicelayer dev and the Playground.

🔌

Need the brain behind a firewall, or no SDK code at all? Register it as a brain connector. Moving from 0.x? connector.llm({ onQuery }) becomes onQuery on the agent — see migrating to 1.0.