npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@signalwire/typescript-agents

v0.1.0

Published

SignalWire AI Agents SDK for TypeScript/Node.js

Readme

SignalWire AI Agents SDK for TypeScript

Build AI voice agents as HTTP microservices that serve SWML documents and handle SWAIG function callbacks.

Quick Start

npm install
npm run build
import { AgentBase, SwaigFunctionResult } from 'signalwire-agents';

const agent = new AgentBase({
  name: 'my-agent',
  route: '/',
  basicAuth: ['user', 'pass'],
});

agent.setPromptText('You are a helpful assistant.');

agent.defineTool({
  name: 'get_time',
  description: 'Get the current time',
  parameters: {},
  handler: () => new SwaigFunctionResult(`It is ${new Date().toLocaleTimeString()}`),
});

agent.run(); // Starts HTTP server on port 3000

Point your SignalWire phone number's SWML webhook at http://user:pass@your-host:3000/ and the agent will answer calls.

How It Works

When a call comes in, SignalWire requests a SWML document from your agent's HTTP endpoint. The SDK builds this document with your configured prompt, tools, languages, and call flow verbs. During the call, the AI can invoke your tools via SWAIG callbacks to the same server.

SignalWire ──GET /──> Agent (returns SWML JSON)
SignalWire ──POST /swaig──> Agent (calls your tool handlers)
SignalWire ──POST /post_prompt──> Agent (sends call summary)

Installation

npm install signalwire-agents

Requires Node.js >= 18.

Core Concepts

AgentBase

The main class. Each agent is an HTTP server that serves SWML and handles tool callbacks.

const agent = new AgentBase({
  name: 'my-agent',        // Agent name (required)
  route: '/',               // HTTP route path
  port: 3000,               // Server port
  basicAuth: ['u', 'p'],    // HTTP basic auth credentials
  autoAnswer: true,         // Auto-answer incoming calls
  recordCall: false,        // Record calls
  recordFormat: 'mp4',      // Recording format
  nativeFunctions: [],      // Platform-native functions (e.g., 'check_time')
});

Auth credentials can also be set via environment variables SWML_BASIC_AUTH_USER and SWML_BASIC_AUTH_PASSWORD. If neither is provided, credentials are auto-generated and logged at startup.

Tools (SWAIG Functions)

Tools let the AI perform actions during a call. Define them with defineTool():

agent.defineTool({
  name: 'lookup_order',
  description: 'Look up an order by ID',
  parameters: {
    order_id: { type: 'string', description: 'The order ID' },
  },
  handler: async (args, fullBody) => {
    const order = await db.findOrder(args.order_id);
    return new SwaigFunctionResult(`Order ${order.id}: status is ${order.status}`);
  },
});

Handlers receive the parsed arguments and the full request body. They can be async. The return value must be a SwaigFunctionResult.

Secure Tools

Mark a tool as secure: true to require per-call HMAC tokens:

agent.defineTool({
  name: 'charge_card',
  description: 'Charge the customer credit card',
  parameters: { amount: { type: 'string', description: 'Amount to charge' } },
  secure: true,
  handler: (args) => new SwaigFunctionResult(`Charged $${args.amount}`),
});

SwaigFunctionResult

The response builder for tool handlers. Carries response text plus structured platform actions. All methods return this for chaining.

// Simple response
return new SwaigFunctionResult('Done.');

// Transfer the call
return new SwaigFunctionResult('Transferring you now.')
  .connect('+15551234567');

// Multiple actions
return new SwaigFunctionResult('Noted.')
  .updateGlobalData({ last_action: 'sms' })
  .sendSms({
    toNumber: '+15551234567',
    fromNumber: '+15559876543',
    body: 'Your order has shipped!',
  });

// Hang up
return new SwaigFunctionResult('Goodbye!').hangup();

Available actions: connect, hangup, hold, stop, waitForUser, say, playBackgroundFile, stopBackgroundFile, sendSms, recordCall, stopRecordCall, tap, stopTap, joinRoom, joinConference, sipRefer, pay, executeSwml, switchContext, swmlChangeStep, swmlChangeContext, updateGlobalData, removeGlobalData, setMetadata, removeMetadata, toggleFunctions, simulateUserInput, replaceInHistory, addDynamicHints, clearDynamicHints, setEndOfSpeechTimeout, executeRpc, and more.

Prompts

Raw Text

agent.setPromptText('You are a helpful assistant.');

Prompt Object Model (POM)

Build structured prompts with sections, bullets, and subsections:

agent.promptAddSection('Role', {
  body: 'You are a customer support agent.',
});

agent.promptAddSection('Rules', {
  bullets: [
    'Always be polite',
    'Never share internal data',
    'Escalate if unsure',
  ],
});

agent.promptAddSection('Guidelines', {
  body: 'Follow these interaction patterns:',
  subsections: [
    { title: 'Greeting', body: 'Always start with a warm greeting.' },
    { title: 'Closing', body: 'Ask if there is anything else before ending.' },
  ],
});

You can also append to existing sections:

agent.promptAddToSection('Rules', { bullet: 'Keep responses under 2 sentences' });
agent.promptAddSubsection('Guidelines', 'Tone', { body: 'Use a conversational tone.' });

DataMap (Server-Side Tools)

DataMap tools execute on the SignalWire platform without hitting your server. They map API responses to AI-consumable text using templates:

import { DataMap, SwaigFunctionResult } from 'signalwire-agents';

const weather = new DataMap('get_weather')
  .purpose('Get weather for a city')
  .parameter('city', 'string', 'City name', { required: true })
  .webhook('GET', 'https://wttr.in/${lc:args.city}?format=j1')
  .output(new SwaigFunctionResult('Weather: ${response.current_condition[0].temp_F}°F'))
  .fallbackOutput(new SwaigFunctionResult('Could not fetch weather.'));

agent.registerSwaigFunction(weather.toSwaigFunction());

Or use the helper for simple cases:

import { createSimpleApiTool } from 'signalwire-agents';

const joke = createSimpleApiTool({
  name: 'get_joke',
  url: 'https://official-joke-api.appspot.com/random_joke',
  responseTemplate: '${response.setup} ... ${response.punchline}',
});

agent.registerSwaigFunction(joke.toSwaigFunction());

Contexts & Steps

Define multi-step conversation workflows where the AI follows a structured sequence:

const ctx = agent.defineContexts();
const flow = ctx.addContext('default');

flow.addStep('greeting', { task: 'Greet the user and ask how you can help.' })
  .setStepCriteria('User has stated their request')
  .setFunctions('none')
  .setValidSteps(['help', 'goodbye']);

flow.addStep('help', { task: 'Help the user with their request.' })
  .setFunctions(['lookup_order', 'check_status'])
  .setValidSteps(['goodbye']);

flow.addStep('goodbye', { task: 'Thank the user and end the call.' })
  .setEnd(true);

Steps support gather_info for structured data collection:

flow.addStep('collect_info')
  .setText('Collect the caller information.')
  .setGatherInfo({ outputKey: 'caller_info' })
  .addGatherQuestion({ key: 'name', question: 'What is your name?' })
  .addGatherQuestion({ key: 'email', question: 'What is your email?', type: 'string', confirm: true })
  .setValidSteps(['process']);

Call Flow (5-Phase Rendering)

Control what happens at each stage of the call:

// Phase 1: Before answering (e.g., play ringing)
agent.addPreAnswerVerb('play', { urls: ['ring:us'], auto_answer: false });

// Phase 2: Answer (automatic if autoAnswer: true)

// Phase 3: After answer, before AI (e.g., welcome message, recording)
agent.addPostAnswerVerb('play', { url: 'say:Welcome!' });

// Phase 4: AI conversation (automatic - built from your prompt, tools, etc.)

// Phase 5: After AI ends (e.g., cleanup)
agent.addPostAiVerb('hangup', {});

AI Configuration

// Speech hints for better recognition
agent.addHints(['SignalWire', 'SWML', 'SWAIG']);

// Languages and voices
agent.addLanguage({ name: 'English', code: 'en-US', voice: 'rachel' });

// Pronunciation corrections
agent.addPronunciation({ replace: 'API', with: 'A P I' });

// LLM parameters
agent.setParam('temperature', 0.7);
agent.setParams({ top_p: 0.9, frequency_penalty: 0.3 });
agent.setPromptLlmParams({ temperature: 0.5 }); // per-prompt params

// Global data accessible to all tools
agent.setGlobalData({ company: 'Acme Corp', plan: 'enterprise' });

// Native platform functions
agent.setNativeFunctions(['check_time']);

Dynamic Configuration

Customize agent behavior per-request based on caller info, query params, or headers:

agent.setDynamicConfigCallback(async (queryParams, bodyParams, headers, agentCopy) => {
  const copy = agentCopy as AgentBase;

  // Different prompt based on query param
  if (queryParams['lang'] === 'es') {
    copy.setPromptText('Responde en español.');
    copy.setLanguages([{ name: 'Spanish', code: 'es-ES', voice: 'polly.Lucia' }]);
  }

  // Add caller-specific data
  const callerId = bodyParams['caller_id_number'] as string;
  if (callerId) {
    copy.setGlobalData({ caller_phone: callerId });
  }
});

Each request gets an ephemeral copy of the agent, so modifications don't affect other callers.

Post-Prompt (Call Summaries)

Get a summary after each call:

agent.setPostPrompt('Summarize this call as JSON with: topic, resolution, follow_up_needed');

Override onSummary() in a subclass to process summaries:

class MyAgent extends AgentBase {
  async onSummary(summary: Record<string, unknown> | null, rawData: Record<string, unknown>) {
    console.log('Call summary:', summary);
    // Save to database, send notification, etc.
  }
}

Multi-Agent Server

Host multiple agents under a single HTTP server:

import { AgentServer, AgentBase } from 'signalwire-agents';

const support = new AgentBase({ name: 'support', route: '/support', basicAuth: ['u', 'p'] });
support.setPromptText('You are a support agent.');

const sales = new AgentBase({ name: 'sales', route: '/sales', basicAuth: ['u', 'p'] });
sales.setPromptText('You are a sales agent.');

const server = new AgentServer({ port: 3000 });
server.register(support);
server.register(sales);
server.run();

The root / returns a JSON listing of all registered agents. Health checks are at /health and /ready.

HTTP Endpoints

Each agent exposes these endpoints:

| Endpoint | Method | Description | |----------|--------|-------------| | / | GET/POST | Returns SWML document | | /swaig | GET/POST | SWAIG function dispatcher | | /post_prompt | GET/POST | Post-prompt summary handler | | /health | GET | Health check | | /ready | GET | Readiness check |

All endpoints (except health/ready) require basic auth.

Environment Variables

| Variable | Description | |----------|-------------| | PORT | Server port (default: 3000) | | SWML_BASIC_AUTH_USER | Basic auth username | | SWML_BASIC_AUTH_PASSWORD | Basic auth password | | SWML_PROXY_URL_BASE | Proxy/tunnel base URL for webhook URLs |

Documentation

Comprehensive guides and API reference are in the docs/ directory:

| Guide | Description | |-------|-------------| | Agent Guide | Getting started — creating agents, prompts, tools, call flow, dynamic config | | Architecture | System design, component relationships, SWML rendering pipeline | | SWAIG Reference | Complete SwaigFunctionResult API — all call control, audio, data, and action methods | | DataMap Guide | Server-side tools — webhooks, expressions, templates, environment variables | | Contexts & Steps | Multi-step conversation workflows, navigation, gather_info | | Skills Guide | Skills framework — 18 built-in skills, custom skill development | | Prefabs Guide | Pre-built agents — InfoGatherer, Survey, FAQ, Concierge, Receptionist | | CLI Guide | Testing tool — dump SWML, execute tools, simulate serverless | | Configuration | Constructor options, environment variables, ConfigLoader, AuthHandler | | Security | Authentication, SSL/TLS, CORS, rate limiting, production checklist | | Serverless Guide | Deploy to AWS Lambda, Google Cloud Functions, Azure Functions, CGI | | API Reference | Complete reference for every exported class, method, and interface |

Examples

See the examples/ directory:

Run any example:

npx tsx examples/simple-agent.ts

Development

npm run build        # Compile TypeScript
npm test             # Run tests
npm run test:watch   # Watch mode
npm run dev          # Watch + rebuild

License

MIT