> ## Documentation Index
> Fetch the complete documentation index at: https://docs2.openclaw.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Плагин голосовых вызовов

Голосовые вызовы для OpenClaw через плагин: исходящие уведомления, многоходовые
диалоги, полнодуплексная голосовая связь в реальном времени, потоковая транскрипция и
входящие вызовы с политиками списков разрешений.

**Провайдеры:** `mock` (для разработки, без сети), `plivo` (Voice API + передача XML +
распознавание речи GetInput), `telnyx` (Call Control v2), `twilio` (Programmable Voice +
Media Streams).

> [!NOTE]
>
> Плагин голосовых вызовов работает **внутри процесса Gateway**. Если вы используете
> удалённый Gateway, установите и настройте плагин на компьютере, где запущен
> Gateway, а затем перезапустите Gateway, чтобы загрузить его.

## Быстрый старт

**1. Установите плагин**

> [tab] Из npm
>
> ```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
> openclaw plugins install @openclaw/voice-call
> ```

> [tab] Из локальной папки (для разработки)
>
> ```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
> PLUGIN_SRC=./path/to/local/voice-call-plugin
> openclaw plugins install "$PLUGIN_SRC"
> cd "$PLUGIN_SRC" && pnpm install
> ```

Используйте пакет без указания версии, чтобы следовать текущему тегу выпуска. Закрепляйте точную
версию только тогда, когда требуется воспроизводимая установка. После этого перезапустите Gateway,
чтобы плагин загрузился.

**2. Настройте провайдера и Webhook**

Задайте конфигурацию в `plugins.entries.voice-call.config` (см. раздел
[Конфигурация](#configuration) ниже). Необходимый минимум: `provider`, учётные данные
провайдера, `fromNumber` и общедоступный URL-адрес Webhook.

**3. Проверьте настройку**

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
openclaw voicecall setup
openclaw voicecall setup --json
```

Проверяет, включён ли плагин, учётные данные провайдера, доступность Webhook и
активен ли только один аудиорежим (`streaming` или `realtime`).

**4. Выполните быструю проверку**

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
openclaw voicecall smoke
openclaw voicecall smoke --to "+15555550123"
```

По умолчанию обе команды выполняются в тестовом режиме без реального вызова. Добавьте `--yes`, чтобы совершить короткий исходящий
вызов с уведомлением:

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
openclaw voicecall smoke --to "+15555550123" --yes
```

> [!WARNING]
>
> Для Twilio, Telnyx и Plivo настройка должна определять **общедоступный URL-адрес Webhook**.
> Если `publicUrl`, URL-адрес туннеля, URL-адрес Tailscale или резервный адрес сервера
> указывает на loopback-адрес или адрес в частной сети, настройка завершается с ошибкой вместо
> запуска провайдера, который не сможет получать Webhook от оператора связи.

## Конфигурация

Если `enabled: true`, но у выбранного провайдера отсутствуют учётные данные, при запуске Gateway
в журнал записывается предупреждение о незавершённой настройке с перечислением отсутствующих ключей, а
среда выполнения не запускается. При использовании команд, вызовов RPC и инструментов агента они по-прежнему возвращают
точный список отсутствующих параметров конфигурации.

> [!NOTE]
>
> Учётные данные голосовых вызовов поддерживают SecretRefs. `plugins.entries.voice-call.config.twilio.authToken`, `plugins.entries.voice-call.config.realtime.providers.*.apiKey`, `plugins.entries.voice-call.config.streaming.providers.*.apiKey` и `plugins.entries.voice-call.config.tts.providers.*.apiKey` разрешаются через стандартный интерфейс SecretRef; см. [Интерфейс учётных данных SecretRef](https://docs2.openclaw.ai/ru/reference/secretref-credential-surface.md).

```json5 theme={"theme":{"light":"min-light","dark":"min-dark"}}
{
  plugins: {
    entries: {
      "voice-call": {
        enabled: true,
        config: {
          provider: "twilio", // or "telnyx" | "plivo" | "mock"
          fromNumber: "+15550001234", // or TWILIO_FROM_NUMBER for Twilio
          toNumber: "+15550005678",
          sessionScope: "per-phone", // per-phone | per-call
          numbers: {
            "+15550009999": {
              inboundGreeting: "Silver Fox Cards, how can I help?",
              responseSystemPrompt: "You are a concise baseball card specialist.",
              tts: {
                providers: {
                  openai: { speakerVoice: "alloy" },
                },
              },
            },
          },

          twilio: {
            accountSid: "ACxxxxxxxx",
            authToken: "...",
            // region: "ie1", // optional: us1 | ie1 | au1; defaults to us1
          },
          telnyx: {
            apiKey: "...",
            connectionId: "...",
            // Telnyx webhook public key from the Mission Control Portal
            // (Base64; can also be set via TELNYX_PUBLIC_KEY).
            publicKey: "...",
          },
          plivo: {
            authId: "MAxxxxxxxxxxxxxxxxxxxx",
            authToken: "...",
          },

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

          // Webhook security (recommended for tunnels/proxies)
          webhookSecurity: {
            allowedHosts: ["voice.example.com"],
            trustedProxyIPs: ["100.64.0.1"],
          },

          // Public exposure (pick one)
          // publicUrl: "https://example.ngrok.app/voice/webhook",
          // tunnel: { provider: "ngrok" },
          // tailscale: { mode: "funnel", path: "/voice/webhook" },

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

          streaming: { enabled: true /* Twilio only; see Streaming transcription */ },
          realtime: { enabled: false /* see Realtime voice conversations */ },
        },
      },
    },
  },
}
```

### Справочник по конфигурации

Ключи верхнего уровня в `plugins.entries.voice-call.config`, не показанные выше:

| Ключ | Значение по умолчанию | Примечания |
| - | - | - |
| `enabled` | `false` | Главный переключатель включения и выключения. |
| `inboundPolicy` | `"disabled"` | `disabled` \| `allowlist` \| `pairing` \| `open`. См. [Входящие вызовы](#inbound-calls). |
| `allowFrom` | `[]` | Список разрешений E.164 для `inboundPolicy: "allowlist"`. |
| `maxDurationSeconds` | `300` | Жёсткое ограничение длительности каждого вызова, применяемое независимо от состояния ответа. |
| `staleCallReaperSeconds` | `120` | См. [Очистка устаревших вызовов](#stale-call-reaper). `0` отключает её. |
| `silenceTimeoutMs` | `800` | Определение тишины в конце речи для классического режима (не в реальном времени). |
| `transcriptTimeoutMs` | `180000` | Максимальное время ожидания транскрипции речи звонящего перед прекращением обработки хода. |
| `ringTimeoutMs` | `30000` | Время ожидания ответа для исходящих вызовов. |
| `maxConcurrentCalls` | `1` | Исходящие вызовы сверх этого ограничения отклоняются. |
| `outbound.notifyHangupDelaySec` | `3` | Число секунд ожидания после TTS перед автоматическим завершением вызова в режиме уведомления. |
| `skipSignatureVerification` | `false` | Только для локального тестирования; никогда не включайте в рабочей среде. |
| `store` | не задано | Переопределяет стандартный путь журнала вызовов `~/.openclaw/voice-calls`. |
| `agentId` | `"main"` | Агент, используемый для генерации ответов и хранения сеансов. |
| `responseModel` | не задано | Переопределяет стандартную модель для классических ответов (не в реальном времени). |
| `responseSystemPrompt` | генерируется | Пользовательская системная инструкция для классических ответов. |
| `responseTimeoutMs` | `30000` | Время ожидания генерации классического ответа (мс). |

По умолчанию Twilio использует конечную точку REST для US1. Чтобы обрабатывать вызовы в поддерживаемом
регионе за пределами США, задайте для `twilio.region` значение `ie1` или `au1` и используйте учётные данные из
этого региона. См.
[руководство Twilio по использованию REST API в регионах за пределами США](https://www.twilio.com/docs/global-infrastructure/using-the-twilio-rest-api-in-a-non-us-region).

> [accordion] Примечания о доступности и безопасности провайдеров
>
> * Для Twilio, Telnyx и Plivo требуется **общедоступный** URL-адрес Webhook.
> * `mock` — локальный провайдер для разработки (без сетевых вызовов).
> * Для Telnyx требуется `telnyx.publicKey` (или `TELNYX_PUBLIC_KEY`), если `skipSignatureVerification` не имеет значения true.
> * `skipSignatureVerification` предназначен только для локального тестирования.
> * На бесплатном тарифе ngrok задайте для `publicUrl` точный URL-адрес ngrok; проверка подписи выполняется всегда.
> * `tunnel.allowNgrokFreeTierLoopbackBypass: true` разрешает Webhook Twilio с недействительными подписями **только** когда `tunnel.provider="ngrok"`, а `serve.bind` является loopback-адресом (локальный агент ngrok). Только для локальной разработки.
> * URL-адреса бесплатного тарифа ngrok могут изменяться или добавлять промежуточную страницу; если `publicUrl` изменится, проверка подписей Twilio завершится ошибкой. Для рабочей среды предпочтительнее стабильный домен или туннель Tailscale.

> [accordion] Ограничения потоковых подключений
>
> * `streaming.preStartTimeoutMs` (по умолчанию `5000`) закрывает сокеты, которые так и не отправили допустимый кадр `start`.
> * `streaming.maxPendingConnections` (по умолчанию `32`) ограничивает общее количество неаутентифицированных сокетов до начала сеанса.
> * `streaming.maxPendingConnectionsPerIp` (по умолчанию `4`) ограничивает количество неаутентифицированных сокетов до начала сеанса для каждого IP-адреса источника.
> * `streaming.maxConnections` (по умолчанию `128`) ограничивает общее количество открытых сокетов медиапотоков (ожидающих и активных).

> [accordion] Миграции устаревшей конфигурации
>
> При разборе конфигурации эти устаревшие ключи автоматически нормализуются, а в журнал
> записывается предупреждение с путём замены; совместимый переходный слой будет удалён в будущем
> выпуске (`2026.6.0`), поэтому запустите `openclaw doctor --fix`, чтобы преобразовать сохранённую
> конфигурацию в каноническую форму:
>
> * `provider: "log"` → `provider: "mock"`
> * `twilio.from` → `fromNumber`
> * `streaming.sttProvider` → `streaming.provider`
> * `streaming.openaiApiKey` → `streaming.providers.openai.apiKey`
> * `streaming.sttModel` → `streaming.providers.openai.model`
> * `streaming.silenceDurationMs` → `streaming.providers.openai.silenceDurationMs`
> * `streaming.vadThreshold` → `streaming.providers.openai.vadThreshold`
> * `realtime.agentContext.includeSystemPrompt` удалён (контекст реального времени теперь использует сгенерированную инструкцию агента)

## Область действия сеанса

По умолчанию плагин голосовых вызовов использует `sessionScope: "per-phone"`, поэтому повторные вызовы от
одного и того же звонящего сохраняют память диалога. Задайте `sessionScope: "per-call"`, если
каждый вызов через оператора должен начинаться с нового контекста, например для службы приёма,
бронирования, IVR или подключения к Google Meet, где один и тот же номер телефона может
соответствовать разным встречам.

Плагин голосовых вызовов сохраняет сгенерированные ключи сеансов в пространстве имён настроенного агента
(`agent:<agentId>:voice:*`). Явно заданные необработанные ключи интеграции разрешаются в
том же пространстве имён: канонический ключ `agent:<configuredAgentId>:*` сохраняет этого
владельца и учитывает псевдонимы основного `session.mainKey`/глобальной области; сторонний или
некорректный ввод `agent:*` помещается в область настроенного агента как непрозрачный ключ;
`global` и `unknown` остаются глобальными сигнальными значениями.

## Голосовые диалоги в реальном времени

`realtime` выбирает провайдера полнодуплексной голосовой связи в реальном времени для звука активного вызова.
Он не связан с `streaming`, который только перенаправляет звук провайдерам
транскрипции в реальном времени.

> [!WARNING]
>
> `realtime.enabled` нельзя использовать совместно с `streaming.enabled`. Выберите один
> аудиорежим для каждого вызова.

Текущее поведение среды выполнения:

* `realtime.enabled` поддерживается для Twilio и Telnyx.
* `realtime.provider` является необязательным. Если значение не задано, Voice Call использует первого зарегистрированного провайдера голосовой связи в реальном времени.
* Встроенные провайдеры голосовой связи в реальном времени: Google Gemini Live (`google`) и OpenAI (`openai`), зарегистрированные соответствующими плагинами провайдеров.
* Необработанная конфигурация, принадлежащая провайдеру, находится в `realtime.providers.<providerId>`.
* По умолчанию Voice Call предоставляет общий инструмент реального времени `openclaw_agent_consult`. Модель реального времени может вызывать его, когда абонент просит провести более глубокий анализ, получить актуальную информацию или использовать обычные инструменты OpenClaw.
* `realtime.consultPolicy` при необходимости добавляет указания о том, когда модель реального времени должна вызывать `openclaw_agent_consult`.
* `realtime.agentContext.enabled` по умолчанию отключён. Если он включён, при настройке сеанса Voice Call добавляет в инструкции провайдера реального времени ограниченный контекст с идентичностью агента и выбранными файлами рабочего пространства.
* `realtime.fastContext.enabled` по умолчанию отключён. Если он включён, Voice Call сначала ищет вопрос консультации в индексированном контексте памяти и сеанса и возвращает найденные фрагменты модели реального времени в пределах `realtime.fastContext.timeoutMs`, а затем обращается к полноценному агенту консультации, только если `realtime.fastContext.fallbackToConsult` имеет значение true.
* Если `realtime.provider` указывает на незарегистрированного провайдера или ни один провайдер голосовой связи в реальном времени вообще не зарегистрирован, Voice Call записывает предупреждение в журнал и пропускает обработку мультимедиа в реальном времени вместо завершения всего плагина с ошибкой.
* `inboundPolicy` не должен иметь значение `"disabled"`, когда `realtime.enabled` имеет значение true; `validateProviderConfig` отклоняет такое сочетание.
* Ключи сеанса консультации при наличии повторно используют сохранённый сеанс вызова, а иначе используют настроенный `sessionScope` (по умолчанию `per-phone` или `per-call` для изолированных вызовов).

### Политика инструментов

`realtime.toolPolicy` управляет запуском консультации:

| Политика | Поведение |
| - | - |
| `safe-read-only` | Предоставить инструмент консультации и ограничить обычного агента инструментами `read`, `web_search`, `web_fetch`, `x_search`, `memory_search` и `memory_get`. |
| `owner` | Предоставить инструмент консультации и разрешить обычному агенту использовать стандартную политику инструментов агента. |
| `none` | Не предоставлять инструмент консультации. Пользовательские `realtime.tools` по-прежнему передаются провайдеру реального времени. |

`realtime.consultPolicy` управляет только инструкциями модели реального времени:

| Политика | Указания |
| - | - |
| `auto` | Сохранить стандартный промпт и позволить провайдеру решать, когда вызывать инструмент консультации. |
| `substantive` | Отвечать напрямую на простые связующие реплики в диалоге и обращаться за консультацией перед ответами, требующими фактов, памяти, инструментов или контекста. |
| `always` | Обращаться за консультацией перед каждым содержательным ответом. |

### Голосовой контекст агента

Включите `realtime.agentContext`, если голосовой шлюз должен звучать как
настроенный агент OpenClaw без полного цикла обращения к агенту за консультацией
для обычных реплик. Контекстный пакет добавляется один раз при создании сеанса
реального времени, поэтому он не увеличивает задержку для каждой реплики. Вызовы
`openclaw_agent_consult` по-прежнему запускают полноценного агента OpenClaw и должны использоваться
для работы с инструментами, получения актуальной информации, поиска в памяти или состояния рабочего пространства.

```json5 theme={"theme":{"light":"min-light","dark":"min-dark"}}
{
  plugins: {
    entries: {
      "voice-call": {
        config: {
          agentId: "main",
          realtime: {
            enabled: true,
            provider: "google",
            toolPolicy: "safe-read-only",
            consultPolicy: "substantive",
            agentContext: {
              enabled: true,
              maxChars: 6000,
              includeIdentity: true,
              includeWorkspaceFiles: true,
              files: ["SOUL.md", "IDENTITY.md", "USER.md"],
            },
          },
        },
      },
    },
  },
}
```

### Примеры провайдеров реального времени

> [tab] Google Gemini Live
>
> Значения по умолчанию: ключ API из `realtime.providers.google.apiKey`, `GEMINI_API_KEY`
> или `GOOGLE_API_KEY`; модель `gemini-3.1-flash-live-preview`;
> голос `Kore`. `sessionResumption` и `contextWindowCompression` по умолчанию включены
> для более длительных вызовов с возможностью переподключения. Используйте `silenceDurationMs`,
> `startSensitivity` и `endSensitivity`, чтобы настроить более быструю смену реплик
> для телефонного аудио.
>
> ```json5 theme={"theme":{"light":"min-light","dark":"min-dark"}}
> {
>   plugins: {
>     entries: {
>       "voice-call": {
>         config: {
>           provider: "twilio",
>           inboundPolicy: "allowlist",
>           allowFrom: ["+15550005678"],
>           realtime: {
>             enabled: true,
>             provider: "google",
>             instructions: "Говорите кратко. Вызывайте openclaw_agent_consult перед использованием более сложных инструментов.",
>             toolPolicy: "safe-read-only",
>             consultPolicy: "substantive",
>             consultThinkingLevel: "low",
>             consultFastMode: true,
>             agentContext: { enabled: true },
>             providers: {
>               google: {
>                 apiKey: "${GEMINI_API_KEY}",
>                 model: "gemini-3.1-flash-live-preview",
>                 speakerVoice: "Kore",
>                 silenceDurationMs: 500,
>                 startSensitivity: "high",
>               },
>             },
>           },
>         },
>       },
>     },
>   },
> }
> ```

> [tab] OpenAI
>
> ```json5 theme={"theme":{"light":"min-light","dark":"min-dark"}}
> {
>   plugins: {
>     entries: {
>       "voice-call": {
>         config: {
>           realtime: {
>             enabled: true,
>             provider: "openai",
>             providers: {
>               openai: { apiKey: "${OPENAI_API_KEY}" },
>             },
>           },
>         },
>       },
>     },
>   },
> }
> ```

См. разделы [Провайдер Google](https://docs2.openclaw.ai/ru/providers/google.md) и
[Провайдер OpenAI](https://docs2.openclaw.ai/ru/providers/openai.md), посвящённые параметрам голосовой связи
в реальном времени для конкретных провайдеров.

## Потоковая транскрипция

`streaming` подключает Twilio Media Streams к провайдеру транскрипции в реальном времени.
Для классического потокового пути требуется `provider: "twilio"`; конфигурация с
Telnyx, Plivo или mock отклоняется. Для передачи аудио Telnyx в реальном времени вместо этого используется
отдельно аутентифицируемый путь `realtime.enabled`.

Текущее поведение среды выполнения:

* `streaming.provider` является необязательным. Если значение не задано, Voice Call использует первого зарегистрированного провайдера транскрипции в реальном времени.
* Встроенные провайдеры транскрипции в реальном времени: Deepgram (`deepgram`), ElevenLabs (`elevenlabs`), Mistral (`mistral`), OpenAI (`openai`) и xAI (`xai`), зарегистрированные соответствующими плагинами провайдеров.
* Необработанная конфигурация, принадлежащая провайдеру, находится в `streaming.providers.<providerId>`.
* После получения от Twilio принятого сообщения потока `start` Voice Call немедленно регистрирует поток, ставит входящие мультимедийные данные в очередь для обработки провайдером транскрипции, пока тот подключается, и запускает начальное приветствие только после готовности транскрипции в реальном времени.
* Если `streaming.provider` указывает на незарегистрированного провайдера или ни один провайдер не зарегистрирован, Voice Call записывает предупреждение в журнал и пропускает потоковую передачу мультимедиа вместо завершения всего плагина с ошибкой.

### Примеры потоковых провайдеров

> [tab] OpenAI
>
> Значения по умолчанию: ключ API `streaming.providers.openai.apiKey` или
> `OPENAI_API_KEY`; модель `gpt-4o-transcribe`; `silenceDurationMs: 800`;
> `vadThreshold: 0.5`.
>
> ```json5 theme={"theme":{"light":"min-light","dark":"min-dark"}}
> {
>   plugins: {
>     entries: {
>       "voice-call": {
>         config: {
>           streaming: {
>             enabled: true,
>             provider: "openai",
>             streamPath: "/voice/stream",
>             providers: {
>               openai: {
>                 apiKey: "sk-...", // необязательно, если задан OPENAI_API_KEY
>                 model: "gpt-4o-transcribe",
>                 silenceDurationMs: 800,
>                 vadThreshold: 0.5,
>               },
>             },
>           },
>         },
>       },
>     },
>   },
> }
> ```

> [tab] xAI
>
> Значения по умолчанию: ключ API `streaming.providers.xai.apiKey` или `XAI_API_KEY` (если
> ни один не задан, используется профиль аутентификации xAI OAuth); конечная точка
> `wss://api.x.ai/v1/stt`; кодировка `mulaw`; частота дискретизации `8000`;
> `endpointingMs: 800`; `interimResults: true`.
>
> ```json5 theme={"theme":{"light":"min-light","dark":"min-dark"}}
> {
>   plugins: {
>     entries: {
>       "voice-call": {
>         config: {
>           streaming: {
>             enabled: true,
>             provider: "xai",
>             streamPath: "/voice/stream",
>             providers: {
>               xai: {
>                 apiKey: "${XAI_API_KEY}", // необязательно, если задан XAI_API_KEY
>                 endpointingMs: 800,
>                 language: "en",
>               },
>             },
>           },
>         },
>       },
>     },
>   },
> }
> ```

## TTS для вызовов

Voice Call использует основную конфигурацию `messages.tts` для потокового синтеза речи во время
вызовов. Её можно переопределить в конфигурации плагина, используя **ту же структуру** —
она глубоко объединяется с `messages.tts`.

```json5 theme={"theme":{"light":"min-light","dark":"min-dark"}}
{
  tts: {
    provider: "elevenlabs",
    providers: {
      elevenlabs: {
        speakerVoiceId: "pMsXgVXv3BLzUgSXRplE",
        modelId: "eleven_multilingual_v2",
      },
    },
  },
}
```

> [!WARNING]
>
> **Синтез речи Microsoft игнорируется для голосовых вызовов.** Для телефонного синтеза требуется
> провайдер, реализующий вывод для телефонии; провайдер синтеза речи
> Microsoft его не реализует, поэтому при вызовах он пропускается, а вместо него выполняются попытки
> использовать другие провайдеры из цепочки резервных вариантов.

Примечания о поведении:

* Устаревшие ключи `tts.<provider>` в конфигурации плагина (`openai`, `elevenlabs`, `microsoft`, `edge`) исправляются командой `openclaw doctor --fix`; в сохраняемой конфигурации следует использовать `tts.providers.<provider>`.
* Основной TTS используется, когда включена потоковая передача мультимедиа Twilio; в противном случае для вызовов используются встроенные голоса провайдера.
* Если поток мультимедиа Twilio уже активен, Voice Call не переключается на резервный вариант TwiML `<Say>`. Если в этом состоянии телефонный TTS недоступен, запрос воспроизведения завершается с ошибкой вместо смешивания двух путей воспроизведения.
* Когда телефонный TTS переключается на резервного провайдера, Voice Call записывает в журнал предупреждение с цепочкой провайдеров (`from`, `to`, `attempts`) для отладки.
* Когда прерывание речи Twilio или завершение потока очищает очередь ожидающих запросов TTS, поставленные в очередь запросы воспроизведения завершаются, а не зависают, оставляя абонентов в ожидании окончания воспроизведения.

### Примеры TTS

> [tab] Только основной TTS
>
> ```json5 theme={"theme":{"light":"min-light","dark":"min-dark"}}
> {
>   messages: {
>     tts: {
>       provider: "openai",
>       providers: {
>         openai: { speakerVoice: "alloy" },
>       },
>     },
>   },
> }
> ```

> [tab] Переопределение на ElevenLabs (только для звонков)
>
> ```json5 theme={"theme":{"light":"min-light","dark":"min-dark"}}
> {
>   plugins: {
>     entries: {
>       "voice-call": {
>         config: {
>           tts: {
>             provider: "elevenlabs",
>             providers: {
>               elevenlabs: {
>                 apiKey: "elevenlabs_key",
>                 speakerVoiceId: "pMsXgVXv3BLzUgSXRplE",
>                 modelId: "eleven_multilingual_v2",
>               },
>             },
>           },
>         },
>       },
>     },
>   },
> }
> ```

> [tab] Переопределение модели OpenAI (глубокое слияние)
>
> ```json5 theme={"theme":{"light":"min-light","dark":"min-dark"}}
> {
>   plugins: {
>     entries: {
>       "voice-call": {
>         config: {
>           tts: {
>             providers: {
>               openai: {
>                 model: "gpt-4o-mini-tts",
>                 speakerVoice: "marin",
>               },
>             },
>           },
>         },
>       },
>     },
>   },
> }
> ```

## Входящие звонки

По умолчанию политика входящих звонков — `disabled`. Чтобы разрешить входящие звонки, задайте:

```json5 theme={"theme":{"light":"min-light","dark":"min-dark"}}
{
  inboundPolicy: "allowlist",
  allowFrom: ["+15550001234"],
  inboundGreeting: "Здравствуйте! Чем я могу помочь?",
}
```

> [!WARNING]
>
> `inboundPolicy: "allowlist"` — это проверка идентификатора вызывающего абонента с низким уровнем достоверности. Плагин
> нормализует предоставленное провайдером значение `From` и сравнивает его с `allowFrom`.
> Проверка Webhook подтверждает доставку провайдером и целостность полезной нагрузки,
> но **не** подтверждает принадлежность номера вызывающему абоненту PSTN/VoIP. Рассматривайте
> `allowFrom` как фильтрацию идентификатора вызывающего абонента, а не как надёжное подтверждение его личности.

Автоматические ответы используют систему агентов. Настройте их с помощью `responseModel`,
`responseSystemPrompt` и `responseTimeoutMs`.

### Маршрутизация по номерам

Используйте `numbers`, когда один плагин Voice Call принимает звонки на несколько телефонных
номеров и каждый номер должен работать как отдельная линия. Например,
для одного номера можно использовать неформального личного помощника, а для другого — деловой
образ, другого агента ответов и другой голос TTS.

Маршруты выбираются по предоставленному провайдером набранному номеру `To`. Ключи должны
быть номерами в формате E.164. При поступлении звонка Voice Call однократно определяет подходящий
маршрут, сохраняет его в записи звонка и повторно использует эту
итоговую конфигурацию для приветствия, классического пути автоматического ответа, пути
консультации в реальном времени и воспроизведения TTS. Если подходящего маршрута нет, используется глобальная
конфигурация Voice Call. Исходящие звонки не используют `numbers`; при инициировании звонка
явно передавайте адресата исходящего звонка, сообщение и сеанс.

Переопределения маршрутов в настоящее время поддерживают:

* `inboundGreeting`
* `tts`
* `agentId`
* `responseModel`
* `responseSystemPrompt`
* `responseTimeoutMs`

Значение маршрута `tts` глубоко сливается с глобальной конфигурацией Voice Call `tts`, поэтому
обычно достаточно переопределить только голос провайдера:

```json5 theme={"theme":{"light":"min-light","dark":"min-dark"}}
{
  inboundGreeting: "Здравствуйте, вы позвонили на основную линию.",
  responseSystemPrompt: "Вы — голосовой помощник по умолчанию.",
  tts: {
    provider: "openai",
    providers: {
      openai: { speakerVoice: "coral" },
    },
  },
  numbers: {
    "+15550001111": {
      inboundGreeting: "Silver Fox Cards, чем я могу помочь?",
      responseSystemPrompt: "Вы — лаконичный специалист по бейсбольным карточкам.",
      tts: {
        providers: {
          openai: { speakerVoice: "alloy" },
        },
      },
    },
  },
}
```

### Контракт речевого вывода

Для автоматических ответов Voice Call добавляет в системную подсказку строгий контракт речевого вывода,
требующий ответа JSON `{"spoken":"..."}`. Voice Call
защитным образом извлекает текст речи:

* Игнорирует полезные нагрузки, помеченные как содержимое рассуждения или ошибки.
* Анализирует непосредственно JSON, JSON в блоке кода или встроенные ключи `"spoken"`.
* При невозможности анализа использует обычный текст и удаляет начальные абзацы, предположительно содержащие планирование или метаданные.

Благодаря этому воспроизводимая речь содержит только текст, предназначенный для вызывающего абонента,
а текст планирования не попадает в аудио.

### Поведение при начале разговора

Для исходящих звонков `conversation` обработка первого сообщения связана с текущим
состоянием воспроизведения:

* Очистка очереди при перебивании и автоматический ответ подавляются только пока начальное приветствие активно воспроизводится.
* Если начальное воспроизведение завершается ошибкой, звонок возвращается в состояние `listening`, а начальное сообщение остаётся в очереди для повторной попытки.
* Начальное воспроизведение для потоковой передачи Twilio запускается при подключении потока без дополнительной задержки.
* Перебивание прерывает активное воспроизведение и удаляет из очереди ещё не воспроизводимые записи TTS Twilio. Удалённые записи завершаются со статусом пропущенных, поэтому логика последующего ответа может продолжить работу, не ожидая аудио, которое уже не будет воспроизведено.
* Голосовые разговоры в реальном времени используют собственную начальную реплику потока реального времени. Voice Call **не** отправляет устаревшее обновление TwiML `<Say>` для этого начального сообщения, поэтому исходящие сеансы `<Connect><Stream>` остаются подключёнными.

### Льготный период при отключении потока Twilio

Когда медиапоток Twilio отключается, Voice Call ожидает **2000 мс**, прежде чем
автоматически завершить звонок:

* Если поток повторно подключается в течение этого периода, автоматическое завершение отменяется.
* Если после окончания льготного периода поток не регистрируется повторно, звонок завершается, чтобы активные звонки не зависали.

## Очистка устаревших звонков

Используйте `staleCallReaperSeconds` (по умолчанию **120**), чтобы завершать звонки, на которые
не ответили и которые не перешли в состояние активного разговора, например звонки
в режиме уведомления, для которых провайдер так и не отправил завершающий Webhook. Установите значение `0`,
чтобы отключить эту функцию.

Очистка выполняется каждые 30 секунд и завершает только звонки, у которых отсутствует
временная метка `answeredAt` и которые ещё не находятся в конечном состоянии или состоянии активного разговора
(`speaking`/`listening`), поэтому отвеченные разговоры никогда не завершаются
этим таймером; `maxDurationSeconds` (по умолчанию 300) — это отдельное ограничение,
завершающее отвеченные звонки, которые длятся слишком долго.

Для потоков в стиле уведомлений, где операторы могут медленно доставлять Webhook
о звонке или ответе, увеличьте `staleCallReaperSeconds` относительно значения по умолчанию, чтобы медленные, но нормальные
звонки не завершались преждевременно; `120`–`300` секунд — разумный диапазон для промышленной эксплуатации.

```json5 theme={"theme":{"light":"min-light","dark":"min-dark"}}
{
  plugins: {
    entries: {
      "voice-call": {
        config: {
          maxDurationSeconds: 300,
          staleCallReaperSeconds: 120,
        },
      },
    },
  },
}
```

## Безопасность Webhook

Если перед Gateway расположен прокси-сервер или туннель, плагин восстанавливает
публичный URL для проверки подписи. Эти параметры определяют, каким
переадресованным заголовкам следует доверять:

**webhookSecurity.allowedHosts** `string[]`

Разрешённые хосты из переадресованных заголовков.

**webhookSecurity.trustForwardingHeaders** `boolean`

Доверять переадресованным заголовкам без списка разрешённых значений.

**webhookSecurity.trustedProxyIPs** `string[]`

Доверять переадресованным заголовкам, только если удалённый IP-адрес запроса присутствует в списке.

Дополнительные меры защиты:

* Защита Webhook **от повторного воспроизведения** включена для Twilio, Telnyx и Plivo. Повторно отправленные допустимые запросы Webhook подтверждаются, но их побочные эффекты пропускаются.
* Реплики разговора Twilio содержат токен отдельной реплики в обратных вызовах `<Gather>`, поэтому устаревшие или повторно воспроизведённые обратные вызовы речи не могут удовлетворить ожидание более новой расшифровки реплики.
* Неаутентифицированные запросы Webhook отклоняются до чтения тела, если отсутствуют обязательные заголовки подписи провайдера.
* Webhook voice-call использует общий профиль чтения тела до аутентификации (максимальный размер тела 64 КБ, тайм-аут чтения 5 секунд), а также ограничение количества выполняющихся запросов для каждого ключа (по умолчанию 8 одновременных запросов на ключ) до проверки подписи.

Пример со стабильным публичным хостом:

```json5 theme={"theme":{"light":"min-light","dark":"min-dark"}}
{
  plugins: {
    entries: {
      "voice-call": {
        config: {
          publicUrl: "https://voice.example.com/voice/webhook",
          webhookSecurity: {
            allowedHosts: ["voice.example.com"],
          },
        },
      },
    },
  },
}
```

## CLI

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
openclaw voicecall call --to "+15555550123" --message "Привет от OpenClaw"
openclaw voicecall start --to "+15555550123"   # псевдоним для call
openclaw voicecall continue --call-id <id> --message "Есть вопросы?"
openclaw voicecall speak --call-id <id> --message "Одну минуту"
openclaw voicecall dtmf --call-id <id> --digits "ww123456#"
openclaw voicecall end --call-id <id>
openclaw voicecall status --call-id <id>
openclaw voicecall tail
openclaw voicecall latency                      # сводка задержек реплик из журналов
openclaw voicecall expose --mode funnel
```

Если Gateway уже запущен, рабочие команды `voicecall`
делегируются среде выполнения voice-call, принадлежащей Gateway, чтобы CLI не привязывал
второй сервер Webhook. Если Gateway недоступен, команды переключаются на
автономную среду выполнения CLI.

`latency` считывает `calls.jsonl` из стандартного пути хранилища voice-call. Используйте
`--file <path>`, чтобы указать другой журнал, и `--last <n>`, чтобы ограничить
анализ последними N записями (по умолчанию 200). Вывод включает минимальное, максимальное и среднее значения,
p50 и p95 для задержки реплики и времени ожидания прослушивания.

## Инструмент агента

Имя инструмента: `voice_call`.

| Действие | Аргументы |
| - | - |
| `initiate_call` | `message`, `to?`, `mode?`, `dtmfSequence?` |
| `continue_call` | `callId`, `message` |
| `speak_to_user` | `callId`, `message` |
| `send_dtmf` | `callId`, `digits` |
| `end_call` | `callId` |
| `get_status` | `callId` |

Плагин voice-call поставляется с соответствующим навыком агента.

## RPC Gateway

| Метод | Аргументы | Примечания |
| - | - | - |
| `voicecall.initiate` | `to?`, `message`, `mode?`, `sessionKey?`, `requesterSessionKey?` | Если `to` не указан, используется конфигурация `toNumber`. |
| `voicecall.start` | `to`, `message?`, `mode?`, `dtmfSequence?`, `sessionKey?` | Аналогично `initiate`, но также принимает `dtmfSequence` до подключения. |
| `voicecall.continue` | `callId`, `message` | Блокирует выполнение до завершения хода; возвращает расшифровку. |
| `voicecall.continue.start` | `callId`, `message` | Асинхронный вариант: немедленно возвращает `operationId`. |
| `voicecall.continue.result` | `operationId` | Опрашивает ожидающую операцию `voicecall.continue.start` для получения результата. |
| `voicecall.speak` | `callId`, `message` | Воспроизводит речь без ожидания; использует мост реального времени, если `realtime.enabled`. |
| `voicecall.dtmf` | `callId`, `digits` | |
| `voicecall.end` | `callId` | |
| `voicecall.status` | `callId?` | Не указывайте `callId`, чтобы вывести все активные вызовы. |

`dtmfSequence` допустим только с `mode: "conversation"`; вызовы в режиме уведомления,
которым после установления соединения требуется передача цифр, должны использовать
`voicecall.dtmf` после создания вызова.

## Устранение неполадок

### Во время настройки не удаётся открыть доступ к Webhook

Запустите настройку в той же среде, где работает Gateway:

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
openclaw voicecall setup
openclaw voicecall setup --json
```

Для `twilio`, `telnyx` и `plivo` состояние `webhook-exposure` должно быть успешным. Даже
настроенный `publicUrl` не работает, если он указывает на локальное или частное
сетевое пространство, поскольку оператор не может выполнить обратный вызов по таким адресам.
Не используйте `localhost`, `127.0.0.1`, `0.0.0.0`, `10.x`, `172.16.x`-`172.31.x`,
`192.168.x`, `169.254.x`, `fc00::/7`, `fd00::/8` и другие диапазоны
NAT операторского класса в качестве `publicUrl`.

Исходящие вызовы Twilio в режиме уведомления отправляют исходный TwiML `<Say>` непосредственно
в запросе на создание вызова, поэтому первое голосовое сообщение не зависит от
получения TwiML для Webhook службой Twilio. Публичный Webhook по-прежнему необходим для обратных
вызовов состояния, разговорных вызовов, DTMF до подключения, потоков реального времени и
управления вызовом после подключения.

Используйте один способ открытия публичного доступа:

```json5 theme={"theme":{"light":"min-light","dark":"min-dark"}}
{
  plugins: {
    entries: {
      "voice-call": {
        config: {
          publicUrl: "https://voice.example.com/voice/webhook",
          // или
          tunnel: { provider: "ngrok" },
          // или
          tailscale: { mode: "funnel", path: "/voice/webhook" },
        },
      },
    },
  },
}
```

После изменения конфигурации перезапустите или перезагрузите Gateway, затем выполните:

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
openclaw voicecall setup
openclaw voicecall smoke
```

`voicecall smoke` выполняется в тестовом режиме, если не передан `--yes`.

### Ошибка учётных данных провайдера

Проверьте выбранного провайдера и обязательные поля учётных данных:

* Twilio: `twilio.accountSid`, `twilio.authToken` и `fromNumber` либо
  `TWILIO_ACCOUNT_SID`, `TWILIO_AUTH_TOKEN` и `TWILIO_FROM_NUMBER`.
* Telnyx: `telnyx.apiKey`, `telnyx.connectionId`, `telnyx.publicKey` и
  `fromNumber` либо `TELNYX_API_KEY`, `TELNYX_CONNECTION_ID` и
  `TELNYX_PUBLIC_KEY`.
* Plivo: `plivo.authId`, `plivo.authToken` и `fromNumber` либо
  `PLIVO_AUTH_ID` и `PLIVO_AUTH_TOKEN`.

Учётные данные должны находиться на хосте Gateway. Изменение локального профиля оболочки
не влияет на уже работающий Gateway, пока он не будет перезапущен или не перезагрузит
своё окружение.

### Вызовы начинаются, но Webhook от провайдера не поступают

Убедитесь, что в консоли провайдера указан точный URL публичного Webhook:

```text theme={"theme":{"light":"min-light","dark":"min-dark"}}
https://voice.example.com/voice/webhook
```

Затем проверьте состояние среды выполнения:

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
openclaw voicecall status --call-id <id>
openclaw voicecall tail
openclaw logs --follow
```

Распространённые причины:

* `publicUrl` указывает на путь, отличный от `serve.path`.
* URL туннеля изменился после запуска Gateway.
* Прокси перенаправляет запрос, но удаляет или изменяет заголовки хоста или протокола.
* Брандмауэр или DNS направляет публичное имя хоста не на Gateway.
* Gateway был перезапущен без включённого плагина Voice Call.

Если перед Gateway расположен обратный прокси или туннель, задайте
в `webhookSecurity.allowedHosts` публичное имя хоста либо используйте
`webhookSecurity.trustedProxyIPs` для известного адреса прокси. Используйте
`webhookSecurity.trustForwardingHeaders` только тогда, когда граница прокси
находится под вашим контролем.

### Ошибка проверки подписи

Подписи провайдера проверяются относительно публичного URL, который OpenClaw восстанавливает
из входящего запроса. Если проверка подписи завершается ошибкой:

* Убедитесь, что URL Webhook провайдера в точности совпадает с `publicUrl`, включая схему, хост и путь.
* Для URL бесплатного тарифа ngrok обновляйте `publicUrl` при изменении имени хоста туннеля.
* Убедитесь, что прокси сохраняет исходные заголовки хоста и протокола, либо настройте `webhookSecurity.allowedHosts`.
* Не включайте `skipSignatureVerification` за пределами локального тестирования.

### Не удаётся подключиться к Google Meet через Twilio

Google Meet использует этот плагин для подключения через телефонный доступ Twilio. Сначала проверьте Voice
Call:

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
openclaw voicecall setup
openclaw voicecall smoke --to "+15555550123"
```

Затем отдельно проверьте транспорт Google Meet:

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
openclaw googlemeet setup --transport twilio
```

Если Voice Call работает, но участник не подключается к встрече, проверьте
номер телефонного доступа Meet, PIN-код и `--dtmf-sequence`. Телефонный вызов может работать
нормально, даже если встреча отклоняет или игнорирует неверную последовательность DTMF.

Google Meet запускает телефонный сегмент Twilio через `voicecall.start` с
последовательностью DTMF до подключения. Последовательности, производные от PIN-кода, включают
`voiceCall.dtmfDelayMs` плагина Google Meet (по умолчанию **12000 ms**) в качестве начальных
цифр ожидания Twilio, поскольку подсказки телефонного доступа Meet могут поступать с задержкой. Затем Voice Call
перенаправляет вызов обратно на обработку в реальном времени до запроса вступительного приветствия.

Используйте `openclaw logs --follow` для просмотра трассировки этапов в реальном времени. При успешном подключении
Twilio к Meet события регистрируются в следующем порядке:

* Google Meet передаёт подключение через Twilio плагину Voice Call.
* Voice Call сохраняет TwiML с DTMF до подключения.
* Исходный TwiML Twilio обрабатывается и передаётся до начала обработки в реальном времени.
* Voice Call передаёт TwiML реального времени для вызова Twilio.
* Google Meet запрашивает вступительную речь с `voicecall.speak` после задержки, следующей за DTMF.

`openclaw voicecall tail` по-прежнему показывает сохранённые записи вызовов; это полезно для
просмотра состояния вызовов и расшифровок, но не все переходы Webhook и обработки в реальном времени
отображаются там.

### В вызове реального времени отсутствует речь

Убедитесь, что включён только один режим аудио: `realtime.enabled` и
`streaming.enabled` не могут одновременно иметь значение true.

Для вызовов Twilio/Telnyx в реальном времени также проверьте следующее:

* Плагин провайдера реального времени загружен и зарегистрирован.
* `realtime.provider` не задан или содержит имя зарегистрированного провайдера.
* Ключ API провайдера доступен процессу Gateway.
* `openclaw logs --follow` показывает, что TwiML реального времени передан, мост реального времени запущен, а исходное приветствие поставлено в очередь.

## Связанные разделы

* [Режим разговора](https://docs2.openclaw.ai/ru/nodes/talk.md)
* [Преобразование текста в речь](https://docs2.openclaw.ai/ru/tools/tts.md)
* [Голосовая активация](https://docs2.openclaw.ai/ru/nodes/voicewake.md)
