Skip to content

contextablemark/twilio-ag-ui-agent

Repository files navigation

Twilio AG-UI Voice Assistant

A Twilio Conversation Relay integration that uses the AG-UI protocol (v0.0.43) to communicate with backend AI systems. Build voice assistants that work with any AG-UI-compatible backend.

Architecture

Phone Call → Twilio → ConversationRelay → WebSocket → TwilioAgent → AG-UI Protocol → Backend Agent
Component Role
TwilioAgent Translates between Twilio's WebSocket protocol and AG-UI events
HttpAgent AG-UI client for connecting to HTTP/SSE backends with Bearer token auth
AG-UI Protocol Standardized event-based protocol for agent communication (v0.0.43)

Features

  • Real-time voice conversations via Twilio
  • Streaming responses with TTS-optimized buffering
  • Interrupt handling (user can interrupt mid-response)
  • Bearer token authentication for AG-UI backend
  • Configurable conversation modes (stateful/stateless)
  • Works with any AG-UI 0.0.43-compatible backend
  • Deployable to Railway or any Node.js hosting platform
  • Full AG-UI 0.0.43 event coverage (text, tool, run lifecycle, state)

Prerequisites

  • Node.js 20+
  • Twilio account with a phone number
  • AG-UI compatible backend server
  • For local dev: ngrok (for exposing local server to Twilio)

Quick Start

Deploy to Railway

  1. Connect your GitHub repo to Railway
  2. Set required environment variables in the Railway dashboard:
    • AGUI_BACKEND_URL - Your AG-UI backend endpoint
    • AGUI_BEARER_TOKEN - Bearer token for backend authentication
  3. Railway auto-sets PORT and RAILWAY_PUBLIC_DOMAIN
  4. Configure your Twilio phone number webhook to: https://YOUR_RAILWAY_DOMAIN/twiml
  5. Call your Twilio phone number

Local Development

  1. Install dependencies:

    npm install
  2. Configure environment variables:

    cp .env.example .env
    # Edit .env with your values
  3. Start ngrok:

    ngrok http 8080
  4. Update .env:

    NGROK_URL=abc123.ngrok.io
    AGUI_BACKEND_URL=https://your-backend.example.com/chat
    AGUI_BEARER_TOKEN=your-api-key
  5. Start the server:

    npm run dev
  6. Configure Twilio:

    • Set your phone number webhook to: https://YOUR_NGROK_URL/twiml
    • HTTP method: POST
  7. Call your Twilio phone number and start talking!

Configuration

All configuration is via environment variables (no hardcoded URLs or secrets).

Required

Variable Description
AGUI_BACKEND_URL AG-UI backend endpoint (server exits if not set)
AGUI_BEARER_TOKEN Bearer token for AG-UI backend authentication

Deployment

Variable Description
RAILWAY_PUBLIC_DOMAIN Set automatically by Railway
NGROK_URL ngrok domain for local tunnel dev (no https://)
PORT Server port (default: 8080, auto-set by Railway)

Optional

Variable Default Description
WELCOME_GREETING "Hi! I am an AI voice assistant..." Initial greeting message
STATEFUL true true sends full history, false sends only current message
MIN_CHUNK_SIZE 50 Minimum characters to buffer before sending to TTS
LOG_LEVEL info Logging level (info or debug)

Project Structure

├── server.js              # Main server (HTTP, WebSocket, config validation)
├── TwilioAgent.js         # Protocol translation layer
├── TwilioAgent.test.js    # Test suite (24 tests)
├── vitest.config.js       # Test configuration
├── package.json           # Dependencies (AG-UI 0.0.43+)
├── .env.example           # Environment variables template
├── CLAUDE.md              # Claude Code guidance
└── README.md              # This file

How It Works

Twilio WebSocket Protocol

Twilio sends three message types over WebSocket:

  • setup - Initialize the session with a call ID
  • prompt - User's spoken text (speech-to-text result)
  • interrupt - User interrupted the assistant mid-response

Twilio expects responses as:

{ "type": "text", "token": "Hello ", "last": false }
{ "type": "text", "token": "",       "last": true  }

AG-UI Event Protocol (v0.0.43)

The TwilioAgent handles all AG-UI 0.0.43 event types using the EventType enum:

Text message events:

  • TEXT_MESSAGE_START / TEXT_MESSAGE_CONTENT / TEXT_MESSAGE_END - Streaming pattern
  • TEXT_MESSAGE_CHUNK - Self-contained message (auto-transformed to streaming pattern)

Tool events:

  • TOOL_CALL_START / TOOL_CALL_ARGS / TOOL_CALL_END / TOOL_CALL_CHUNK / TOOL_CALL_RESULT

Run lifecycle events:

  • RUN_STARTED / RUN_FINISHED / RUN_ERROR / STEP_STARTED / STEP_FINISHED

State events:

  • STATE_SNAPSHOT / STATE_DELTA / MESSAGES_SNAPSHOT

TTS Buffering

Small text chunks are buffered to prevent TTS engine stuttering:

  1. Deltas accumulate in an output buffer
  2. Buffer flushes when it reaches MIN_CHUNK_SIZE (default 50 chars) or hits a sentence boundary (.!?;:)
  3. Remaining buffer always flushes on message end

Bearer Token Authentication

The AGUI_BEARER_TOKEN is sent as Authorization: Bearer <token> with all requests. The HttpAgent.clone() method preserves headers, so per-call agent instances inherit authentication automatically.

Conversation Modes

Stateful (default): Maintains full conversation history. Sends complete message thread with each request. Provides full context to the backend.

Stateless: Sends only the current user message. Backend must handle context externally (e.g., via threadId). Set STATEFUL=false to enable.

AG-UI Backend Requirements

Your backend must:

  1. Accept RunAgentInput requests (validated against RunAgentInputSchema)
  2. Return AG-UI events via Server-Sent Events (SSE)
  3. Accept Authorization: Bearer <token> header
  4. Be compatible with AG-UI protocol v0.0.43

Testing

npm test              # Run all 24 tests
npm run test:watch    # Watch mode
npm run test:coverage # Coverage report

Endpoints

Endpoint Method Description
/twiml GET/POST Returns TwiML XML for Twilio webhook
/ws WebSocket Twilio ConversationRelay WebSocket connection
/health GET Health check ({ status: "ok", timestamp: "..." })

Troubleshooting

"AGUI_BACKEND_URL environment variable is required"

  • Set AGUI_BACKEND_URL in your environment variables (Railway dashboard or .env file)

"Session error" message on call

  • Check that your public domain is correctly configured (Railway or ngrok)
  • Ensure the server is running and accessible

No response from assistant

  • Verify AGUI_BACKEND_URL points to a running AG-UI backend
  • Check AGUI_BEARER_TOKEN is correct
  • Look at server logs (LOG_LEVEL=debug for details)

Interruptions not working

  • Normal if the assistant is speaking very quickly
  • The system tracks what was actually spoken via utteranceUntilInterrupt

License

MIT

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 3

  •  
  •  
  •