Voice Call (plugin)

Chiamate vocali per Mayros tramite plugin. Supporta notifiche in uscita e conversazioni multi-turno con policy in entrata.

Provider attuali:

  • twilio (Programmable Voice + Media Streams)
  • telnyx (Call Control v2)
  • plivo (Voice API + trasferimento XML + speech GetInput)
  • mock (dev/nessuna rete)

Modello mentale rapido:

  • Installa plugin
  • Riavvia Gateway
  • Configura in plugins.entries.voice-call.config
  • Usa mayros voicecall ... o lo strumento voice_call

Dove gira (locale vs remoto)

Il plugin Voice Call gira dentro il processo Gateway.

Se usi un Gateway remoto, installa/configura il plugin sulla macchina che esegue il Gateway, poi riavvia il Gateway per caricarlo.

Installazione

Opzione A: installa da npm (consigliato)

bash
mayros plugins install @apilium/mayros-voice-call

Riavvia il Gateway dopo.

Opzione B: installa da una cartella locale (dev, senza copia)

bash
mayros plugins install ./extensions/voice-call
cd ./extensions/voice-call && pnpm install

Riavvia il Gateway dopo.

Configurazione

Imposta la configurazione in plugins.entries.voice-call.config:

json5
{
  plugins: {
    entries: {
      "voice-call": {
        enabled: true,
        config: {
          provider: "twilio", // o "telnyx" | "plivo" | "mock"
          fromNumber: "+15550001234",
          toNumber: "+15550005678",

          twilio: {
            accountSid: "ACxxxxxxxx",
            authToken: "...",
          },

          telnyx: {
            apiKey: "...",
            connectionId: "...",
            // Chiave pubblica webhook Telnyx dal Mission Control Portal Telnyx
            // (stringa Base64; può anche essere impostata via TELNYX_PUBLIC_KEY).
            publicKey: "...",
          },

          plivo: {
            authId: "MAxxxxxxxxxxxxxxxxxxxx",
            authToken: "...",
          },

          // Server webhook
          serve: {
            port: 3334,
            path: "/voice/webhook",
          },

          // Sicurezza webhook (consigliato per tunnel/proxy)
          webhookSecurity: {
            allowedHosts: ["voice.example.com"],
            trustedProxyIPs: ["100.64.0.1"],
          },

          // Esposizione pubblica (scegline una)
          // publicUrl: "https://example.ngrok.app/voice/webhook",
          // tunnel: { provider: "ngrok" },
          // tailscale: { mode: "funnel", path: "/voice/webhook" }

          outbound: {
            defaultMode: "notify", // notify | conversation
          },

          streaming: {
            enabled: true,
            streamPath: "/voice/stream",
          },
        },
      },
    },
  },
}

Note:

  • Twilio/Telnyx richiedono un URL webhook raggiungibile pubblicamente.
  • Plivo richiede un URL webhook raggiungibile pubblicamente.
  • mock è un provider dev locale (nessuna chiamata rete).
  • Telnyx richiede telnyx.publicKey (o TELNYX_PUBLIC_KEY) a meno che skipSignatureVerification non sia true.
  • skipSignatureVerification è solo per test locali.
  • Se usi ngrok tier gratuito, imposta publicUrl all'URL ngrok esatto; la verifica firma è sempre applicata.
  • tunnel.allowNgrokFreeTierLoopbackBypass: true permette webhook Twilio con firme non valide solo quando tunnel.provider="ngrok" e serve.bind è loopback (agente locale ngrok). Usa solo per dev locale.
  • Gli URL ngrok tier gratuito possono cambiare o aggiungere comportamento interstitial; se publicUrl deriva, le firme Twilio falliranno. Per produzione, preferisci un dominio stabile o Tailscale funnel.

Reaper chiamate stantie

Usa staleCallReaperSeconds per terminare chiamate che non ricevono mai un webhook terminale (es. chiamate modalità notify che non si completano mai). Il default è 0 (disabilitato).

Range consigliati:

  • Produzione: 120300 secondi per flussi stile notify.
  • Mantieni questo valore più alto di maxDurationSeconds così le chiamate normali possono finire. Un buon punto di partenza è maxDurationSeconds + 30–60 secondi.

Esempio:

json5
{
  plugins: {
    entries: {
      "voice-call": {
        config: {
          maxDurationSeconds: 300,
          staleCallReaperSeconds: 360,
        },
      },
    },
  },
}

Sicurezza Webhook

Quando un proxy o tunnel sta davanti al Gateway, il plugin ricostruisce l'URL pubblico per la verifica firma. Queste opzioni controllano quali header inoltrati sono fidati.

webhookSecurity.allowedHosts allowlist host da header inoltro.

webhookSecurity.trustForwardingHeaders fida header inoltrati senza allowlist.

webhookSecurity.trustedProxyIPs fida solo header inoltrati quando l'IP remoto richiesta corrisponde alla lista.

Esempio con host pubblico stabile:

json5
{
  plugins: {
    entries: {
      "voice-call": {
        config: {
          publicUrl: "https://voice.example.com/voice/webhook",
          webhookSecurity: {
            allowedHosts: ["voice.example.com"],
          },
        },
      },
    },
  },
}

TTS per chiamate

Voice Call usa la configurazione core messages.tts (OpenAI o ElevenLabs) per speech streaming sulle chiamate. Puoi sovrascriverla nella configurazione plugin con la stessa forma — si fonde in profondità con messages.tts.

json5
{
  tts: {
    provider: "elevenlabs",
    elevenlabs: {
      voiceId: "pMsXgVXv3BLzUgSXRplE",
      modelId: "eleven_multilingual_v2",
    },
  },
}

Note:

  • Edge TTS è ignorato per chiamate vocali (l'audio telefonia necessita PCM; output Edge è inaffidabile).
  • Il TTS core è usato quando lo streaming media Twilio è abilitato; altrimenti le chiamate fallback a voci native provider.

Altri esempi

Usa solo TTS core (nessun override):

json5
{
  messages: {
    tts: {
      provider: "openai",
      openai: { voice: "alloy" },
    },
  },
}

Override a ElevenLabs solo per chiamate (mantieni default core altrove):

json5
{
  plugins: {
    entries: {
      "voice-call": {
        config: {
          tts: {
            provider: "elevenlabs",
            elevenlabs: {
              apiKey: "elevenlabs_key",
              voiceId: "pMsXgVXv3BLzUgSXRplE",
              modelId: "eleven_multilingual_v2",
            },
          },
        },
      },
    },
  },
}

Override solo il modello OpenAI per chiamate (esempio deep-merge):

json5
{
  plugins: {
    entries: {
      "voice-call": {
        config: {
          tts: {
            openai: {
              model: "gpt-4o-mini-tts",
              voice: "marin",
            },
          },
        },
      },
    },
  },
}

Chiamate in entrata

La policy in entrata è disabled per default. Per abilitare chiamate in entrata, imposta:

json5
{
  inboundPolicy: "allowlist",
  allowFrom: ["+15550001234"],
  inboundGreeting: "Ciao! Come posso aiutarti?",
}

Le risposte automatiche usano il sistema agente. Regola con:

  • responseModel
  • responseSystemPrompt
  • responseTimeoutMs

CLI

bash
mayros voicecall call --to "+15555550123" --message "Ciao da Mayros"
mayros voicecall continue --call-id <id> --message "Domande?"
mayros voicecall speak --call-id <id> --message "Un momento"
mayros voicecall end --call-id <id>
mayros voicecall status --call-id <id>
mayros voicecall tail
mayros voicecall expose --mode funnel

Strumento agente

Nome strumento: voice_call

Azioni:

  • initiate_call (message, to?, mode?)
  • continue_call (callId, message)
  • speak_to_user (callId, message)
  • end_call (callId)
  • get_status (callId)

Questo repo fornisce una documentazione skill corrispondente in skills/voice-call/SKILL.md.

RPC Gateway

  • voicecall.initiate (to?, message, mode?)
  • voicecall.continue (callId, message)
  • voicecall.speak (callId, message)
  • voicecall.end (callId)
  • voicecall.status (callId)