Skip to main content

Voice-Call Extension: Status & Reference

Self-hosted voice-call extension using Twilio + OpenAI. Functional as a backup/fallback — primary voice agent will be a commercial platform (Retell AI, VAPI, or ElevenLabs). Last updated: 2026-02-20.

Current Status — FUNCTIONAL, PERFORMANCE-LIMITED

Full conversation loop works: phone rings, user answers, hears TTS greeting, speaks, STT transcribes, AI generates response, TTS plays it back, multi-turn conversation sustained.

Performance Problem

Latency: 2-12 seconds per turn (vs 300-800ms on commercial platforms). Root causes — all architectural, not fixable with tuning:
  1. Batch TTStts-openai.ts makes a single REST call, waits for the entire audio buffer, then resamples and streams. No token-level streaming.
  2. Sequential LLM → TTSresponse-generator.ts waits for the full agent response before passing to speak(). No streaming LLM-to-TTS pipe.
  3. Agent overhead — every turn runs the full embedded Pi agent pipeline: memory-lancedb similarity search + injection, hooks, tool calls (first turn reads 5 workspace files = ~10s), session management.
  4. Context bleed — memory plugin injects irrelevant memories (threshold 0.3 is too loose); agent reads SOUL.md/USER.md and dumps persona on first turn instead of responding conversationally.
Commercial platforms (Retell, VAPI, ElevenLabs) solve this with streaming LLM→TTS, voice-optimized models, no agent framework overhead, and purpose-built low-latency infra.

Future Plan

This extension becomes a backup/self-hosted fallback. Primary voice agent will use Retell AI, VAPI, or ElevenLabs, integrated as a separate extension or provider.

Architecture

Key Components

Call Flow (Conversation Mode)

  1. voicecall.initiate → Twilio REST API creates call
  2. Twilio fetches TwiML from webhook → <Connect><Stream> opens media WebSocket
  3. Initial message played via streaming TTS through media stream
  4. User speaks → mu-law audio → OpenAI Realtime STT → transcript
  5. Transcript → embedded Pi agent → AI response text
  6. Response → OpenAI TTS → PCM → resample 8kHz → mu-law → 20ms chunks → media stream
  7. Loop until hangup, “bye”, or timeout

Call States


Configuration

In crocbot.jsonplugins.entries.voice-call.config:
Twilio creds: twilio.accountSid / twilio.authToken or env vars TWILIO_ACCOUNT_SID / TWILIO_AUTH_TOKEN. OpenAI key via OPENAI_API_KEY.

Key Config Fields


CLI & RPC

Agent tool: voice_call with actions initiate_call, end_call, get_status, speak_to_user, continue_call.

Test Coverage

30 tests across 6 files: config parsing, call lifecycle/state machine, phone allowlist (14 tests), Twilio webhooks, signature verification, media stream auth, plugin integration (6 tests).

Upstream Sync

Source: OpenClaw (openclaw/openclaw), local copy at .001_ORIGINAL/. No sync needed (as of 2026-02-21). Upstream CHANGELOG entries after v2026.1.26 are version-only bumps with zero voice-call code changes. Crocbot is ahead of upstream with:
  • sanitizeLogStr() log-injection hardening (3 files)
  • TOCTOU race fixes (cli.ts, manager/store.ts)
  • Telnyx + Plivo providers removed (~1,200 lines; Twilio-only)
  • Webhook diagnostic logging
  • core-bridge.ts reverted to upstream’s single-import approach (Session 3 fix)

Work Completed (Sessions 1-3, 2026-02-20)

Session 1 — Basic Setup

  • Added voice-call plugin entry to crocbot.json
  • Fixed webhookSecurity schema, env var loading, fromNumber config
  • Result: Twilio call connects but no audio (notify mode TTS race)

Session 2 — STT/TTS Pipeline

  • Added streaming.enabled: true (required for conversation mode)
  • Wired tts.textToSpeechTelephony into plugin runtime (types.ts, index.ts)
  • Created src/extensionAPI.ts (build entry not yet added)
  • Result: TTS greeting heard, STT transcribes speech, AI response fails (Missing core module at dist/agents/agent-scope.js)

Session 3 — Full Loop Working

  • Added src/extensionAPI.ts to tsdown.config.ts build entries
  • Reverted core-bridge.ts to upstream’s importCoreExtensionAPI() approach
  • Fixed result.meta?.aborted crash in response-generator.ts
  • Result: Full conversation loop verified — multi-turn voice conversation works
  • Identified performance limitations → decision to use commercial platform as primary

Earlier (2026-02-06, Sessions 1-5) — Upstream Sync

  • Backported all upstream security hardening (webhook security, media stream auth, body size limits, allowlist matching)
  • Removed Telnyx + Plivo providers (Twilio-only simplification)
  • Added 14 allowlist unit tests + 6 plugin integration tests

Troubleshooting